Add the PhoneAccountSuggestionService

Add a service base class for phone acct suggestion. Also add associated
aidl plumbing and a new permission for the service to require.

Test: CTS to come
Bug: 111455117
Change-Id: I30d7d004bdddbab3dfbb5408c07775ea56d490fe
diff --git a/Android.bp b/Android.bp
index 151adf8..bc1facc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -484,6 +484,8 @@
         "telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl",
         "telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl",
         "telecomm/java/com/android/internal/telecom/IInCallService.aidl",
+        "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl",
+        "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl",
         "telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
         "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
         "telephony/java/android/telephony/data/IDataService.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index b7e3d68..b8c3a55 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -23,6 +23,7 @@
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
+    field public static final java.lang.String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
     field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
@@ -4942,6 +4943,14 @@
     ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
   }
 
+  public class PhoneAccountSuggestionService extends android.app.Service {
+    ctor public PhoneAccountSuggestionService();
+    method public void onAccountSuggestionRequest(java.lang.String);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+  }
+
   public final class RemoteConference {
     method public deprecated void setAudioState(android.telecom.AudioState);
   }
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index a39f5e3..4174ad7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -51,6 +51,8 @@
     private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
             "add-or-remove-call-companion-app";
     private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
+    private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
+            "set-phone-acct-suggestion-component";
     private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
     private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
     private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -64,36 +66,37 @@
 
     @Override
     public void onShowUsage(PrintStream out) {
-        out.println(
-                "usage: telecom [subcommand] [options]\n" +
-                "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
-                "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
-                "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
-                "usage: telecom set-test-call-redirection-app <PACKAGE>\n" +
-                "usage: telecom set-test-call-screening-app <PACKAGE>\n" +
-                "usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
-                "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
-                "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" +
-                "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" +
-                "usage: telecom set-default-dialer <PACKAGE>\n" +
-                "usage: telecom get-default-dialer\n" +
-                "usage: telecom get-system-dialer\n" +
-                "usage: telecom wait-on-handlers\n" +
-                "\n" +
-                "telecom set-phone-account-enabled: Enables the given phone account, if it has \n" +
-                " already been registered with Telecom.\n" +
-                "\n" +
-                "telecom set-phone-account-disabled: Disables the given phone account, if it \n" +
-                " has already been registered with telecom.\n" +
-                "\n" +
-                "telecom set-default-dialer: Sets the default dialer to the given component. \n" +
-                "\n" +
-                "telecom get-default-dialer: Displays the current default dialer. \n" +
-                "\n" +
-                "telecom get-system-dialer: Displays the current system dialer. \n" +
-                "\n" +
-                "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
-                );
+        out.println("usage: telecom [subcommand] [options]\n"
+                + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
+                + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
+                + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
+                + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+                + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
+                + "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
+                + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
+                + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
+                + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
+                + " <LABEL> <ADDRESS>\n"
+                + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n"
+                + "usage: telecom set-default-dialer <PACKAGE>\n"
+                + "usage: telecom get-default-dialer\n"
+                + "usage: telecom get-system-dialer\n"
+                + "usage: telecom wait-on-handlers\n"
+                + "\n"
+                + "telecom set-phone-account-enabled: Enables the given phone account, if it has \n"
+                + " already been registered with Telecom.\n"
+                + "\n"
+                + "telecom set-phone-account-disabled: Disables the given phone account, if it \n"
+                + " has already been registered with telecom.\n"
+                + "\n"
+                + "telecom set-default-dialer: Sets the default dialer to the given component. \n"
+                + "\n"
+                + "telecom get-default-dialer: Displays the current default dialer. \n"
+                + "\n"
+                + "telecom get-system-dialer: Displays the current system dialer. \n"
+                + "\n"
+                + "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
+        );
     }
 
     @Override
@@ -134,6 +137,9 @@
             case COMMAND_SET_TEST_AUTO_MODE_APP:
                 runSetTestAutoModeApp();
                 break;
+            case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
+                runSetTestPhoneAcctSuggestionComponent();
+                break;
             case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
                 runRegisterSimPhoneAccount();
                 break;
@@ -216,6 +222,11 @@
         mTelecomService.setTestAutoModeApp(packageName);
     }
 
+    private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
+        final String componentName = nextArg();
+        mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
+    }
+
     private void runUnregisterPhoneAccount() throws RemoteException {
         final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
         mTelecomService.unregisterPhoneAccount(handle);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5154de0..344b74c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1832,6 +1832,15 @@
     <permission android:name="android.permission.BIND_SCREENING_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by a {@link android.telecom.PhoneAccountSuggestionService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature|privileged
+         @SystemApi
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a {@link android.telecom.CallRedirectionService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature|privileged
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
new file mode 100644
index 0000000..e2fa7e4
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 android.telecom;
+
+/**
+ * {@hide}
+  */
+parcelable PhoneAccountSuggestion;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
new file mode 100644
index 0000000..8a91b9e
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java
@@ -0,0 +1,121 @@
+/*
+ * 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 android.telecom;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+import com.android.internal.telecom.IPhoneAccountSuggestionService;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for service that allows system apps to suggest phone accounts for outgoing calls.
+ *
+ * Phone account suggestions allow OEMs to intelligently select phone accounts based on knowledge
+ * about the user's past behavior, carrier billing patterns, or other factors unknown to the AOSP
+ * Telecom system.
+ * OEMs who wish to provide a phone account suggestion service on their device should implement this
+ * service in an app that resides in the /system/priv-app/ directory on their device. For security
+ * reasons, the service's entry {@code AndroidManifest.xml} file must declare the
+ * {@link android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE} permission:
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourServiceName"
+ *          android:permission="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE">
+ *      <intent-filter>
+ *          <action android:name="android.telecom.PhoneAccountSuggestionService"/>
+ *      </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ * Only one system app on each device may implement this service. If multiple system apps implement
+ * this service, none of them will be queried for suggestions.
+ * @hide
+ */
+@SystemApi
+public class PhoneAccountSuggestionService extends Service {
+    /**
+     * The {@link Intent} that must be declared in the {@code intent-filter} element of the
+     * service's manifest entry.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService";
+
+    private IPhoneAccountSuggestionService mInterface = new IPhoneAccountSuggestionService.Stub() {
+        @Override
+        public void onAccountSuggestionRequest(IPhoneAccountSuggestionCallback callback,
+                String number) {
+            mCallbackMap.put(number, callback);
+            PhoneAccountSuggestionService.this.onAccountSuggestionRequest(number);
+        }
+    };
+
+    private final Map<String, IPhoneAccountSuggestionCallback> mCallbackMap =
+            new HashMap<>();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mInterface.asBinder();
+    }
+
+    /**
+     * The system calls this method during the outgoing call flow if it needs account suggestions.
+     *
+     * The implementer of this service must override this method to implement its account suggestion
+     * logic. After preparing the suggestions, the implementation of the service must call
+     * {@link #suggestPhoneAccounts(String, List)} to deliver the suggestions back to the system.
+     *
+     * Note that the system will suspend the outgoing call process after it calls this method until
+     * this service calls {@link #suggestPhoneAccounts}.
+     *
+     * @param number The phone number to provide suggestions for.
+     */
+    public void onAccountSuggestionRequest(@NonNull String number) {}
+
+    /**
+     * The implementation of this service calls this method to deliver suggestions to the system.
+     *
+     * The implementation of this service must call this method after receiving a call to
+     * {@link #onAccountSuggestionRequest(String)}. If no suggestions are available, pass an empty
+     * list as the {@code suggestions} argument.
+     *
+     * @param number The phone number to provide suggestions for.
+     * @param suggestions The list of suggestions.
+     */
+    public final void suggestPhoneAccounts(@NonNull String number,
+            @NonNull List<PhoneAccountSuggestion> suggestions) {
+        IPhoneAccountSuggestionCallback callback = mCallbackMap.remove(number);
+        if (callback == null) {
+            Log.w(this, "No suggestions requested for the number %s", Log.pii(number));
+            return;
+        }
+        try {
+            callback.suggestPhoneAccounts(number, suggestions);
+        } catch (RemoteException e) {
+            Log.w(this, "Remote exception calling suggestPhoneAccounts");
+        }
+    }
+}
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
new file mode 100644
index 0000000..cb14241
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.internal.telecom;
+
+import android.telecom.PhoneAccountSuggestion;
+/**
+ * Internal remote callback interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionCallback{
+    void suggestPhoneAccounts(in String number, in List<PhoneAccountSuggestion> suggestions);
+}
diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
new file mode 100644
index 0000000..0ffab93
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.internal.telecom;
+
+import com.android.internal.telecom.IPhoneAccountSuggestionCallback;
+
+/**
+ * Internal remote interface for a phone acct suggestion service.
+ * @hide
+ */
+oneway interface IPhoneAccountSuggestionService {
+    void onAccountSuggestionRequest(in IPhoneAccountSuggestionCallback callback,
+            in String number);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d64efea..6f1b66a 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -307,6 +307,8 @@
 
     void setTestDefaultCallRedirectionApp(String packageName);
 
+    void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
+
     void setTestDefaultCallScreeningApp(String packageName);
 
     void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);