Expose active calls from in call UI

Dual SIM features need to know what phone account the current call is using because usually only one SIM can be in a call at the same time.

TEST=TAP
Bug: 69675796,72618783
Test: TAP
PiperOrigin-RevId: 194121273
Change-Id: I512eb7aca2050f38449b0a911dea9cee9b5ffdb5
diff --git a/java/com/android/dialer/activecalls/ActiveCallInfo.java b/java/com/android/dialer/activecalls/ActiveCallInfo.java
new file mode 100644
index 0000000..d4f76b3
--- /dev/null
+++ b/java/com/android/dialer/activecalls/ActiveCallInfo.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.dialer.activecalls;
+
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+
+/** Info of an active call */
+@AutoValue
+@SuppressWarnings("Guava")
+public abstract class ActiveCallInfo {
+
+  /** The {@link PhoneAccountHandle} the call is made with */
+  public abstract Optional<PhoneAccountHandle> phoneAccountHandle();
+
+  public static Builder builder() {
+    return new AutoValue_ActiveCallInfo.Builder();
+  }
+
+  /** Builder for {@link ActiveCallInfo}. Only In Call UI should create ActiveCallInfo */
+  @AutoValue.Builder
+  public abstract static class Builder {
+
+    public Builder setPhoneAccountHandle(@Nullable PhoneAccountHandle phoneAccountHandle) {
+      return setPhoneAccountHandle(Optional.fromNullable(phoneAccountHandle));
+    }
+
+    public abstract Builder setPhoneAccountHandle(Optional<PhoneAccountHandle> phoneAccountHandle);
+
+    public abstract ActiveCallInfo build();
+  }
+}
diff --git a/java/com/android/dialer/activecalls/ActiveCalls.java b/java/com/android/dialer/activecalls/ActiveCalls.java
new file mode 100644
index 0000000..600839c
--- /dev/null
+++ b/java/com/android/dialer/activecalls/ActiveCalls.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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.dialer.activecalls;
+
+import android.support.annotation.MainThread;
+import com.google.common.collect.ImmutableList;
+
+/** Exposes information about current active calls to the whole dialer. */
+public interface ActiveCalls {
+
+  /**
+   * Return a list of current active calls. Any call that is not disconnected is regarded as active.
+   * Ordering of elements are not guaranteed.
+   */
+  ImmutableList<ActiveCallInfo> getActiveCalls();
+
+  /** Should only be called by in call UI. */
+  @MainThread
+  void setActiveCalls(ImmutableList<ActiveCallInfo> activeCalls);
+}
diff --git a/java/com/android/dialer/activecalls/ActiveCallsComponent.java b/java/com/android/dialer/activecalls/ActiveCallsComponent.java
new file mode 100644
index 0000000..99e0e94
--- /dev/null
+++ b/java/com/android/dialer/activecalls/ActiveCallsComponent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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.dialer.activecalls;
+
+import android.content.Context;
+import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.inject.IncludeInDialerRoot;
+import dagger.Subcomponent;
+
+/** Component for {@link ActiveCalls} */
+@Subcomponent
+public abstract class ActiveCallsComponent {
+
+  public abstract ActiveCalls activeCalls();
+
+  public static ActiveCallsComponent get(Context context) {
+    return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
+        .activeCallsComponent();
+  }
+
+  /** Used to refer to the root application component. */
+  @IncludeInDialerRoot
+  public interface HasComponent {
+    ActiveCallsComponent activeCallsComponent();
+  }
+}
diff --git a/java/com/android/dialer/activecalls/ActiveCallsModule.java b/java/com/android/dialer/activecalls/ActiveCallsModule.java
new file mode 100644
index 0000000..4d7f448
--- /dev/null
+++ b/java/com/android/dialer/activecalls/ActiveCallsModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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.dialer.activecalls;
+
+import com.android.dialer.activecalls.impl.ActiveCallsImpl;
+import com.android.dialer.inject.DialerVariant;
+import com.android.dialer.inject.InstallIn;
+import dagger.Binds;
+import dagger.Module;
+import javax.inject.Singleton;
+
+/** Module for {@link ActiveCallsComponent} */
+@Module
+@InstallIn(variants = DialerVariant.DIALER_TEST) // TODO(weijiaxu): put all variants.
+public abstract class ActiveCallsModule {
+
+  @Singleton
+  @Binds
+  public abstract ActiveCalls to(ActiveCallsImpl impl);
+}
diff --git a/java/com/android/dialer/activecalls/impl/ActiveCallsImpl.java b/java/com/android/dialer/activecalls/impl/ActiveCallsImpl.java
new file mode 100644
index 0000000..3449cc8
--- /dev/null
+++ b/java/com/android/dialer/activecalls/impl/ActiveCallsImpl.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.dialer.activecalls.impl;
+
+import android.support.annotation.MainThread;
+import com.android.dialer.activecalls.ActiveCallInfo;
+import com.android.dialer.activecalls.ActiveCalls;
+import com.android.dialer.common.Assert;
+import com.google.common.collect.ImmutableList;
+import javax.inject.Inject;
+
+/** Implementation of {@link ActiveCalls} */
+public class ActiveCallsImpl implements ActiveCalls {
+
+  ImmutableList<ActiveCallInfo> activeCalls = ImmutableList.of();
+
+  @Inject
+  ActiveCallsImpl() {}
+
+  @Override
+  public ImmutableList<ActiveCallInfo> getActiveCalls() {
+    return activeCalls;
+  }
+
+  @Override
+  @MainThread
+  public void setActiveCalls(ImmutableList<ActiveCallInfo> activeCalls) {
+    Assert.isMainThread();
+    this.activeCalls = Assert.isNotNull(activeCalls);
+  }
+}
diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
index 21a282d..e102189 100644
--- a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
+++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
@@ -17,6 +17,7 @@
 package com.android.dialer.binary.aosp;
 
 import com.android.bubble.stub.StubBubbleModule;
+import com.android.dialer.activecalls.ActiveCallsModule;
 import com.android.dialer.binary.basecomponent.BaseDialerRootComponent;
 import com.android.dialer.calllog.CallLogModule;
 import com.android.dialer.calllog.config.CallLogConfigModule;
@@ -49,6 +50,7 @@
 @Singleton
 @Component(
   modules = {
+    ActiveCallsModule.class,
     CallLogModule.class,
     CallLogConfigModule.class,
     CommandLineModule.class,
diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
index 11e952c..75ddaf7 100644
--- a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
+++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
@@ -17,6 +17,7 @@
 package com.android.dialer.binary.basecomponent;
 
 import com.android.bubble.BubbleComponent;
+import com.android.dialer.activecalls.ActiveCallsComponent;
 import com.android.dialer.calllog.CallLogComponent;
 import com.android.dialer.calllog.config.CallLogConfigComponent;
 import com.android.dialer.calllog.database.CallLogDatabaseComponent;
@@ -50,7 +51,8 @@
  * from this component.
  */
 public interface BaseDialerRootComponent
-    extends BluetoothDeviceProviderComponent.HasComponent,
+    extends ActiveCallsComponent.HasComponent,
+        BluetoothDeviceProviderComponent.HasComponent,
         BubbleComponent.HasComponent,
         CallLocationComponent.HasComponent,
         CallLogComponent.HasComponent,
diff --git a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
index 0da2f95..bdbdeb9 100644
--- a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
+++ b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
@@ -17,6 +17,7 @@
 package com.android.dialer.binary.google;
 
 import com.android.bubble.stub.StubBubbleModule;
+import com.android.dialer.activecalls.ActiveCallsModule;
 import com.android.dialer.binary.basecomponent.BaseDialerRootComponent;
 import com.android.dialer.calllog.CallLogModule;
 import com.android.dialer.calllog.config.CallLogConfigModule;
@@ -52,6 +53,7 @@
 @Singleton
 @Component(
   modules = {
+    ActiveCallsModule.class,
     CallLocationModule.class,
     CallLogModule.class,
     CallLogConfigModule.class,
diff --git a/java/com/android/dialer/commandline/CommandLineModule.java b/java/com/android/dialer/commandline/CommandLineModule.java
index 9155787..c78de21 100644
--- a/java/com/android/dialer/commandline/CommandLineModule.java
+++ b/java/com/android/dialer/commandline/CommandLineModule.java
@@ -16,6 +16,7 @@
 
 package com.android.dialer.commandline;
 
+import com.android.dialer.commandline.impl.ActiveCallsCommand;
 import com.android.dialer.commandline.impl.BlockingCommand;
 import com.android.dialer.commandline.impl.CallCommand;
 import com.android.dialer.commandline.impl.Echo;
@@ -45,6 +46,7 @@
     private final Echo echo;
     private final BlockingCommand blockingCommand;
     private final CallCommand callCommand;
+    private final ActiveCallsCommand activeCallsCommand;
 
     @Inject
     AospCommandInjector(
@@ -52,12 +54,14 @@
         Version version,
         Echo echo,
         BlockingCommand blockingCommand,
-        CallCommand callCommand) {
+        CallCommand callCommand,
+        ActiveCallsCommand activeCallsCommand) {
       this.help = help;
       this.version = version;
       this.echo = echo;
       this.blockingCommand = blockingCommand;
       this.callCommand = callCommand;
+      this.activeCallsCommand = activeCallsCommand;
     }
 
     public CommandSupplier.Builder inject(CommandSupplier.Builder builder) {
@@ -66,6 +70,7 @@
       builder.addCommand("echo", echo);
       builder.addCommand("blocking", blockingCommand);
       builder.addCommand("call", callCommand);
+      builder.addCommand("activecalls", activeCallsCommand);
       return builder;
     }
   }
diff --git a/java/com/android/dialer/commandline/impl/ActiveCallsCommand.java b/java/com/android/dialer/commandline/impl/ActiveCallsCommand.java
new file mode 100644
index 0000000..81641ed
--- /dev/null
+++ b/java/com/android/dialer/commandline/impl/ActiveCallsCommand.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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.dialer.commandline.impl;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import com.android.dialer.activecalls.ActiveCallsComponent;
+import com.android.dialer.commandline.Arguments;
+import com.android.dialer.commandline.Command;
+import com.android.dialer.inject.ApplicationContext;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import javax.inject.Inject;
+
+/** Manipulates {@link com.android.dialer.activecalls.ActiveCalls} */
+public class ActiveCallsCommand implements Command {
+
+  private final Context appContext;
+
+  @Inject
+  ActiveCallsCommand(@ApplicationContext Context appContext) {
+    this.appContext = appContext;
+  }
+
+  @NonNull
+  @Override
+  public String getShortDescription() {
+    return "manipulate active calls";
+  }
+
+  @NonNull
+  @Override
+  public String getUsage() {
+    return "activecalls list";
+  }
+
+  @Override
+  public ListenableFuture<String> run(Arguments args) throws IllegalCommandLineArgumentException {
+    if (args.getPositionals().isEmpty()) {
+      return Futures.immediateFuture(getUsage());
+    }
+
+    String command = args.getPositionals().get(0);
+
+    switch (command) {
+      case "list":
+        return Futures.immediateFuture(
+            ActiveCallsComponent.get(appContext).activeCalls().getActiveCalls().toString());
+      default:
+        throw new IllegalCommandLineArgumentException("unknown command " + command);
+    }
+  }
+}
diff --git a/java/com/android/incallui/ActiveCallsCallListListener.java b/java/com/android/incallui/ActiveCallsCallListListener.java
new file mode 100644
index 0000000..ce9f9a3
--- /dev/null
+++ b/java/com/android/incallui/ActiveCallsCallListListener.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.incallui;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import com.android.dialer.activecalls.ActiveCallInfo;
+import com.android.dialer.activecalls.ActiveCallsComponent;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.State;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+/** Updates {@link com.android.dialer.activecalls.ActiveCalls} */
+@SuppressWarnings("Guava")
+public class ActiveCallsCallListListener implements CallList.Listener {
+
+  private final Context appContext;
+
+  ActiveCallsCallListListener(Context appContext) {
+    this.appContext = appContext;
+  }
+
+  @Override
+  public void onIncomingCall(DialerCall call) {}
+
+  @Override
+  public void onUpgradeToVideo(DialerCall call) {}
+
+  @Override
+  public void onSessionModificationStateChange(DialerCall call) {}
+
+  @Override
+  public void onCallListChange(CallList callList) {
+    ImmutableList.Builder<ActiveCallInfo> activeCalls = ImmutableList.builder();
+    for (DialerCall call : callList.getAllCalls()) {
+      if (call.getState() != State.DISCONNECTED) {
+        activeCalls.add(
+            ActiveCallInfo.builder()
+                .setPhoneAccountHandle(Optional.fromNullable(call.getAccountHandle()))
+                .build());
+      }
+    }
+    ActiveCallsComponent.get(appContext).activeCalls().setActiveCalls(activeCalls.build());
+  }
+
+  @Override
+  public void onDisconnect(DialerCall call) {}
+
+  @Override
+  public void onWiFiToLteHandover(DialerCall call) {}
+
+  @Override
+  public void onHandoverToWifiFailed(DialerCall call) {}
+
+  @Override
+  public void onInternationalCallOnWifi(@NonNull DialerCall call) {}
+}
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
index 5e08c69..a67dab5 100644
--- a/java/com/android/incallui/InCallPresenter.java
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -197,6 +197,7 @@
   private InCallCameraManager inCallCameraManager;
   private FilteredNumberAsyncQueryHandler filteredQueryHandler;
   private CallList.Listener spamCallListListener;
+  private CallList.Listener activeCallsListener;
   /** Whether or not we are currently bound and waiting for Telecom to send us a new call. */
   private boolean boundAndWaitingForOutgoingCall;
   /** Determines if the InCall UI is in fullscreen mode or not. */
@@ -383,6 +384,8 @@
         new SpamCallListListener(
             context, DialerExecutorComponent.get(context).dialerExecutorFactory());
     this.callList.addListener(spamCallListListener);
+    activeCallsListener = new ActiveCallsCallListListener(context);
+    this.callList.addListener(activeCallsListener);
 
     VideoPauseController.getInstance().setUp(this);
 
@@ -858,6 +861,7 @@
           callList.getActiveOrBackgroundCall() != null || callList.getOutgoingCall() != null;
       inCallActivity.dismissKeyguard(hasCall);
     }
+
     Trace.endSection();
   }
 
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
index c153503..9dfe7ab 100644
--- a/java/com/android/incallui/call/DialerCall.java
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -157,7 +157,7 @@
   private String lastForwardedNumber;
   private boolean isCallForwarded;
   private String callSubject;
-  private PhoneAccountHandle phoneAccountHandle;
+  @Nullable private PhoneAccountHandle phoneAccountHandle;
   @CallHistoryStatus private int callHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN;
   private boolean isSpam;
   private boolean isBlocked;