Add support for setSystemSelectionChannels

Create API plumbing for setSystemSelectionChannels.
Also update testapps to handle calling the method through the testapp.

Bug: 144595103
Test: manual, atest TelephonyManagerTest
Change-Id: I2dcffb3e5100cb4bfac23e82c9b26fa42c2d5c31
Merged-In: I2dcffb3e5100cb4bfac23e82c9b26fa42c2d5c31
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 009ea43..a4616e9 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -131,6 +131,7 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.DefaultPhoneNotifier;
 import com.android.internal.telephony.HalVersion;
+import com.android.internal.telephony.IBooleanConsumer;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.ITelephony;
@@ -189,6 +190,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * Implementation of the ITelephony interface.
@@ -274,6 +276,8 @@
     private static final int EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE = 77;
     private static final int CMD_SET_ICC_LOCK_ENABLED = 78;
     private static final int EVENT_SET_ICC_LOCK_ENABLED_DONE = 79;
+    private static final int CMD_SET_SYSTEM_SELECTION_CHANNELS = 80;
+    private static final int EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE = 81;
     private static final int CMD_GET_CALL_FORWARDING = 83;
     private static final int EVENT_GET_CALL_FORWARDING_DONE = 84;
     private static final int CMD_SET_CALL_FORWARDING = 85;
@@ -1336,6 +1340,23 @@
                     }
                     notifyRequester(request);
                     break;
+                case CMD_SET_SYSTEM_SELECTION_CHANNELS: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE, request);
+                    Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> args =
+                            (Pair<List<RadioAccessSpecifier>, Consumer<Boolean>>) request.argument;
+                    request.phone.setSystemSelectionChannels(args.first, onCompleted);
+                    break;
+                }
+                case EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> args =
+                            (Pair<List<RadioAccessSpecifier>, Consumer<Boolean>>) request.argument;
+                    args.second.accept(ar.exception == null);
+                    notifyRequester(request);
+                    break;
+                }
                 case EVENT_SET_FORBIDDEN_PLMNS_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -8158,6 +8179,39 @@
     }
 
     @Override
+    public void setSystemSelectionChannels(List<RadioAccessSpecifier> specifiers,
+            int subscriptionId, IBooleanConsumer resultCallback) {
+        enforceModifyPermission();
+        long token = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subscriptionId);
+            if (phone == null) {
+                try {
+                    if (resultCallback != null) {
+                        resultCallback.accept(false);
+                    }
+                } catch (RemoteException e) {
+                    // ignore
+                }
+                return;
+            }
+            Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> argument =
+                    Pair.create(specifiers, (x) -> {
+                        try {
+                            if (resultCallback != null) {
+                                resultCallback.accept(x);
+                            }
+                        } catch (RemoteException e) {
+                            // ignore
+                        }
+                    });
+            sendRequestAsync(CMD_SET_SYSTEM_SELECTION_CHANNELS, argument, phone, null);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
     public boolean isMvnoMatched(int subId, int mvnoType, @NonNull String mvnoMatchData) {
         IccRecords iccRecords = UiccController.getInstance().getIccRecords(
                 SubscriptionManager.getPhoneId(subId), UiccController.APP_FAM_3GPP);
diff --git a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
index 097c90a..ccb5639 100644
--- a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
+++ b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
@@ -19,12 +19,19 @@
 import android.content.Context;
 import android.telephony.NumberVerificationCallback;
 import android.telephony.PhoneNumberRange;
+import android.telephony.RadioAccessSpecifier;
+import android.text.TextUtils;
 import android.widget.Toast;
 
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 class ParameterParser {
     private static ParameterParser sInstance;
@@ -42,6 +49,9 @@
                 put(PhoneNumberRange.class, ParameterParser::parsePhoneNumberRange);
                 put(Executor.class, s -> parseExecutor(s));
                 put(NumberVerificationCallback.class, s -> parseNumberVerificationCallback(s));
+                put(Consumer.class, s -> parseConsumer(s));
+                put(List.class, s -> parseList(s));
+                put(RadioAccessSpecifier.class, s -> parseRadioAccessSpecifier(s));
             }};
 
     private ParameterParser(Context context) {
@@ -49,6 +59,7 @@
     }
 
     Object executeParser(Class type, String input) {
+        if ("null".equals(input)) return null;
         return mParsers.getOrDefault(type, s -> null).apply(input);
     }
 
@@ -64,6 +75,57 @@
         return mContext.getMainExecutor();
     }
 
+    private Consumer parseConsumer(String input) {
+        return o -> Toast.makeText(mContext, "Consumer: " + String.valueOf(o), Toast.LENGTH_SHORT)
+                .show();
+    }
+
+    /**
+     * input format: (ran)/(band1)+(band2)+.../(chan1)+(chan2)+...
+     * @return
+     */
+    private RadioAccessSpecifier parseRadioAccessSpecifier(String input) {
+        String[] parts = input.split("/");
+        int ran = Integer.parseInt(parts[0]);
+        String[] bandStrings = parts[1].split("\\+");
+        int[] bands = new int[bandStrings.length];
+        String[] chanStrings = parts[2].split("\\+");
+        int[] chans = new int[chanStrings.length];
+
+        for (int i = 0; i < bands.length; i++) {
+            bands[i] = Integer.parseInt(bandStrings[i]);
+        }
+
+        for (int i = 0; i < chans.length; i++) {
+            chans[i] = Integer.parseInt(chanStrings[i]);
+        }
+        return new RadioAccessSpecifier(ran, bands, chans);
+    }
+
+    private List parseList(String input) {
+        if (TextUtils.isEmpty(input)) {
+            return Collections.emptyList();
+        }
+        String[] components = input.split(" ");
+        String className = components[0];
+        Class c;
+        try {
+            c = mContext.getClassLoader().loadClass(className);
+        } catch (ClassNotFoundException e) {
+            Toast.makeText(mContext, "Invalid class " + className,
+                    Toast.LENGTH_SHORT).show();
+            return null;
+        }
+        if (!mParsers.containsKey(c)) {
+            Toast.makeText(mContext, "Cannot parse " + className,
+                    Toast.LENGTH_SHORT).show();
+            return null;
+        }
+        return Arrays.stream(components).skip(1)
+                .map(mParsers.get(c))
+                .collect(Collectors.toList());
+    }
+
     private NumberVerificationCallback parseNumberVerificationCallback(String input) {
         return new NumberVerificationCallback() {
             @Override