Support Gba Api

Added api to support generic authentication architecture, and the test
application

Bug: 154865133
Test: atest cts/tests/tests/telephony/current/src/android/telephony/gba/cts/
Test: manual by GbaTestApp

Change-Id: Ib82637056e7daa451c644bb7a33ed308707b6b53
diff --git a/testapps/GbaTestApp/Android.bp b/testapps/GbaTestApp/Android.bp
new file mode 100644
index 0000000..cb6df4e
--- /dev/null
+++ b/testapps/GbaTestApp/Android.bp
@@ -0,0 +1,26 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// 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.
+
+android_test {
+    name: "GbaTestApp",
+    static_libs: [
+        "androidx.appcompat_appcompat",
+	"androidx-constraintlayout_constraintlayout",
+	"ub-uiautomator",
+    ],
+    srcs: ["src/**/*.java"],
+    javacflags: ["-parameters"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/testapps/GbaTestApp/AndroidManifest.xml b/testapps/GbaTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..8554461
--- /dev/null
+++ b/testapps/GbaTestApp/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.phone.testapps.gbatestapp">
+
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.BIND_GBA_SERVICE" />
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:theme="@style/Theme.AppCompat"
+        android:supportsRtl="true">
+        <service
+            android:name=".TestGbaService"
+            android:directBootAware="true"
+            android:permission="android.permission.BIND_GBA_SERVICE"
+            android:enabled="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.telephony.gba.GbaService"/>
+            </intent-filter>
+        </service>
+
+        <activity android:name=".MainActivity"
+            android:enabled="true"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/testapps/GbaTestApp/res/layout/fragment_carrier_config.xml b/testapps/GbaTestApp/res/layout/fragment_carrier_config.xml
new file mode 100644
index 0000000..f15fa2a
--- /dev/null
+++ b/testapps/GbaTestApp/res/layout/fragment_carrier_config.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/black"
+    tools:context=".ui.main.CarrierConfigFragment">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/service_package_name" />
+
+        <EditText
+            android:id="@+id/editServicePackageName"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="0"
+            android:ems="10"
+            android:gravity="start|top"
+            android:inputType="textMultiLine" />
+
+        <TextView
+            android:id="@+id/textTestLabel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/service_release_time" />
+
+        <EditText
+            android:id="@+id/editServiceReleaseTime"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ems="10"
+            android:inputType="numberSigned" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/layout_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:orientation="vertical">
+
+        <Button
+            android:id="@+id/carrier_config_clear"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_clear" />
+
+        <Button
+            android:id="@+id/carrier_config_done"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_done" />
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/testapps/GbaTestApp/res/layout/fragment_service_config.xml b/testapps/GbaTestApp/res/layout/fragment_service_config.xml
new file mode 100644
index 0000000..50090c2
--- /dev/null
+++ b/testapps/GbaTestApp/res/layout/fragment_service_config.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/black"
+    tools:context=".ui.main.ServiceConfigFragment">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_above="@id/layout_buttons"
+        android:orientation="vertical">
+
+        <CheckBox
+            android:id="@+id/checkBoxResult"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/response_success" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/response_key" />
+
+        <EditText
+            android:id="@+id/editKey"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ems="10"
+            android:inputType="textPersonName" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:editable="false"
+            android:text="@string/response_btid" />
+
+        <EditText
+            android:id="@+id/editBTid"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ems="10"
+            android:inputType="textPersonName" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/response_fail_reason" />
+
+        <EditText
+            android:id="@+id/editFailReason"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ems="10"
+            android:inputType="numberSigned" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/layout_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_gravity="bottom"
+        android:orientation="vertical">
+
+        <Button
+            android:id="@+id/service_config_clear"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_clear" />
+
+        <Button
+            android:id="@+id/service_config_done"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_done" />
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/testapps/GbaTestApp/res/layout/fragment_test_config.xml b/testapps/GbaTestApp/res/layout/fragment_test_config.xml
new file mode 100644
index 0000000..d8016f0
--- /dev/null
+++ b/testapps/GbaTestApp/res/layout/fragment_test_config.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/black"
+    tools:context=".ui.main.TestConfigFragment">
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginBottom="8dp"
+        android:scrollbarStyle="outsideInset"
+        app:layout_constraintBottom_toTopOf="@id/layout_buttons"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/request_app_type" />
+
+            <EditText
+                android:id="@+id/editAppType"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ems="10"
+                android:inputType="numberSigned" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/request_naf_url" />
+
+            <EditText
+                android:id="@+id/editUrl"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ems="10"
+                android:inputType="textUri" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/request_org" />
+
+            <EditText
+                android:id="@+id/editOrg"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ems="10"
+                android:inputType="numberSigned" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/request_security_protocol" />
+
+            <EditText
+                android:id="@+id/editSpId"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ems="10"
+                android:inputType="numberSigned" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/request_tls_cipher_suite" />
+
+            <EditText
+                android:id="@+id/editTlsCs"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ems="10"
+                android:inputType="numberSigned" />
+
+            <CheckBox
+                android:id="@+id/checkBoxForce"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/request_force_bootstrapping" />
+
+        </LinearLayout>
+    </ScrollView>
+
+    <LinearLayout
+        android:id="@+id/layout_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="false"
+        android:layout_gravity="bottom"
+        android:orientation="vertical"
+        app:layout_constraintBottom_toBottomOf="parent">
+
+        <Button
+            android:id="@+id/client_config_clear"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_clear" />
+
+        <Button
+            android:id="@+id/client_config_done"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_done" />
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/GbaTestApp/res/layout/main_activity.xml b/testapps/GbaTestApp/res/layout/main_activity.xml
new file mode 100644
index 0000000..1dfb73b
--- /dev/null
+++ b/testapps/GbaTestApp/res/layout/main_activity.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity" />
\ No newline at end of file
diff --git a/testapps/GbaTestApp/res/layout/main_fragment.xml b/testapps/GbaTestApp/res/layout/main_fragment.xml
new file mode 100644
index 0000000..33bb6e1
--- /dev/null
+++ b/testapps/GbaTestApp/res/layout/main_fragment.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.main.MainFragment">
+
+    <LinearLayout
+        android:id="@+id/layout_config"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:orientation="vertical"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/label_settings" />
+
+        <Button
+            android:id="@+id/carrier_config_change_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/label_carrier" />
+
+        <Button
+            android:id="@+id/service_config"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/label_service" />
+
+        <Button
+            android:id="@+id/client_config"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/label_test" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/layout_test"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:orientation="vertical"
+        app:layout_constraintBottom_toTopOf="@id/layout_exit"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/layout_config">
+
+        <Button
+            android:id="@+id/send_request"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_running" />
+
+        <TextView
+            android:id="@+id/textTestLabel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/label_test_result" />
+        <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical" >
+
+                <TextView
+                    android:id="@+id/viewTestOutput"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"/>
+            </LinearLayout>
+        </ScrollView>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/layout_exit"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_gravity="bottom"
+        android:orientation="vertical"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <Button
+            android:id="@+id/test_exit"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/button_name_exit" />
+    </LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/GbaTestApp/res/values/colors.xml b/testapps/GbaTestApp/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/testapps/GbaTestApp/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/testapps/GbaTestApp/res/values/strings.xml b/testapps/GbaTestApp/res/values/strings.xml
new file mode 100644
index 0000000..e74c181
--- /dev/null
+++ b/testapps/GbaTestApp/res/values/strings.xml
@@ -0,0 +1,30 @@
+<resources>
+    <string name="app_name">GbaTestApp</string>
+    <string name="label_settings">Settings</string>
+    <string name="label_carrier">Carrier Config</string>
+    <string name="label_service">Service Config</string>
+    <string name="label_test">Test Config</string>
+    <string name="button_name_running">Running</string>
+    <string name="button_name_exit">Exit</string>
+    <string name="label_test_result">Test Result</string>
+    <string name="button_name_clear">Reset</string>
+    <string name="button_name_done">Done</string>
+    <string name="title_activity_carrier_config">CarrierConfigActivity</string>
+    <string name="title_activity_service_config">ServiceConfigActivity</string>
+    <string name="title_activity_test_config">TestConfigActivity</string>
+    <string name="service_package_name">Package name of GBA service</string>
+    <string name="service_release_time">How long to release service after calling</string>
+    <string name="request_app_type">UICC App Type</string>
+    <string name="request_naf_url">Network application function (NAF) URL</string>
+    <string name="request_force_bootstrapping">Force Bootstrapping?</string>
+    <string name="request_org">Organization Code</string>
+    <string name="request_security_protocol">UA Security Protocol ID</string>
+    <string name="request_tls_cipher_suite">TLS Cipher Suite ID</string>
+    <string name="response_success">GBA Auth Success?</string>
+    <string name="response_fail_reason">Fail Reason ID</string>
+    <string name="response_key">GBA Key (CK + IK)</string>
+    <string name="response_btid">Bootstrapping Transaction Identifier (B-TID)</string>
+    <string name="sample_naf">3GPP-bootstrapping@naf1.operator.com</string>
+    <string name="sample_btid">(B-TID)</string>
+    <string name="sample_key">6629fae49393a05397450978507c4ef1</string>
+</resources>
diff --git a/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/MainActivity.java b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/MainActivity.java
new file mode 100644
index 0000000..72cbf5c
--- /dev/null
+++ b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/MainActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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.phone.testapps.gbatestapp;
+
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.phone.testapps.gbatestapp.ui.main.MainFragment;
+
+/** main activity of the gba test app */
+public class MainActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main_activity);
+        if (savedInstanceState == null) {
+            getSupportFragmentManager().beginTransaction()
+                    .replace(R.id.container, MainFragment.newInstance())
+                    .commitNow();
+        }
+    }
+}
diff --git a/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/Settings.java b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/Settings.java
new file mode 100644
index 0000000..eaa424a
--- /dev/null
+++ b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/Settings.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2020 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.phone.testapps.gbatestapp;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.util.Locale;
+
+/** class to load and save the settings */
+public class Settings {
+
+    private static final String TAG = "SETTINGS";
+
+    private static final String PREF_CARRIER_CONFIG = "pref_carrier_config";
+    private static final String KEY_SERVICE_PACKAGE = "key_service_package";
+    private static final String KEY_RELEASE_TIME = "key_release_time";
+
+    private static final String PREF_TEST_CONFIG = "pref_test_config";
+    private static final String KEY_APP_TYPE = "key_app_type";
+    private static final String KEY_NAF_URL = "key_naf_url";
+    private static final String KEY_FORCE_BT = "key_force_bt";
+    private static final String KEY_ORG = "org";
+    private static final String KEY_SP_ID = "key_sp_id";
+    private static final String KEY_TLS_CS = "key_tls_cs";
+
+    private static final String PREF_SERVICE_CONFIG = "pref_carrier_config";
+    private static final String KEY_AUTH_RESULT = "key_auth_result";
+    private static final String KEY_GBA_KEY = "key_gba_key";
+    private static final String KEY_B_TID = "key_b_tid";
+    private static final String KEY_FAIL_REASON = "key_fail_reason";
+
+    private ITelephony mTelephony;
+    private int mSubId;
+    private String mServicePackageName;
+    private int mReleaseTime;
+    private int mAppType;
+    private String mNafUrl;
+    private boolean mForceBootstrap;
+    private int mOrg;
+    private int mSpId;
+    private int mTlsCs;
+    private boolean mIsAuthSuccess;
+    private String mGbaKey;
+    private String mBTid;
+    private int mFailReason;
+
+    private static Settings sInstance;
+
+    private Settings(Context cxt) {
+        mTelephony = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
+                .getTelephonyServiceManager().getTelephonyServiceRegisterer().get());
+        mSubId = SubscriptionManager.getDefaultSubscriptionId();
+        SharedPreferences sharedPref = cxt.getSharedPreferences(
+                PREF_CARRIER_CONFIG, Context.MODE_PRIVATE);
+        mServicePackageName = loadServicePackageName(mSubId, sharedPref);
+        mReleaseTime = loadReleaseTime(mSubId, sharedPref);
+
+        sharedPref = cxt.getSharedPreferences(PREF_TEST_CONFIG, Context.MODE_PRIVATE);
+        mAppType = sharedPref.getInt(KEY_APP_TYPE, TelephonyManager.APPTYPE_SIM);
+        mNafUrl = sharedPref.getString(KEY_NAF_URL, null);
+        mForceBootstrap = sharedPref.getBoolean(KEY_FORCE_BT, false);
+        mOrg = sharedPref.getInt(KEY_ORG, 0);
+        mSpId = sharedPref.getInt(KEY_SP_ID, 0);
+        mTlsCs = sharedPref.getInt(KEY_TLS_CS, 0);
+
+        sharedPref = cxt.getSharedPreferences(PREF_SERVICE_CONFIG, Context.MODE_PRIVATE);
+        mIsAuthSuccess = sharedPref.getBoolean(KEY_AUTH_RESULT, false);
+        mFailReason = sharedPref.getInt(KEY_FAIL_REASON, 0);
+        mGbaKey = sharedPref.getString(KEY_GBA_KEY, null);
+        mBTid = sharedPref.getString(KEY_B_TID, null);
+    }
+
+    /** Get the instance of Settings*/
+    public static Settings getSettings(Context cxt) {
+        if (sInstance == null) {
+            sInstance = new Settings(cxt);
+        }
+
+        return sInstance;
+    }
+
+    /** update carrier config settings */
+    public void updateCarrierConfig(Context cxt, String packageName, int releaseTime) {
+        new Thread(() -> {
+            synchronized (PREF_CARRIER_CONFIG) {
+
+                if (TextUtils.equals(mServicePackageName, packageName)
+                        && (mReleaseTime == releaseTime)) {
+                    return;
+                }
+
+                if (!TextUtils.equals(mServicePackageName, packageName)) {
+                    mServicePackageName = packageName;
+
+                    try {
+                        mTelephony.setBoundGbaServiceOverride(mSubId, packageName);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "fail to set package name due to " + e);
+                    }
+
+                }
+
+                if (mReleaseTime != releaseTime) {
+                    mReleaseTime = releaseTime;
+
+                    try {
+                        mTelephony.setGbaReleaseTimeOverride(mSubId, releaseTime);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "fail to set release time due to " + e);
+                    }
+                }
+
+                SharedPreferences sharedPref = cxt.getSharedPreferences(
+                        PREF_CARRIER_CONFIG, Context.MODE_PRIVATE);
+                SharedPreferences.Editor editor = sharedPref.edit();
+                editor.putString(KEY_SERVICE_PACKAGE, packageName);
+                editor.putInt(KEY_RELEASE_TIME, releaseTime);
+                editor.commit();
+            }
+        }).start();
+    }
+
+    /** get the config of gba service package name */
+    public String getServicePackageName() {
+        synchronized (PREF_CARRIER_CONFIG) {
+            return mServicePackageName;
+        }
+    }
+
+    /** get the config of gba release time */
+    public int getReleaseTime() {
+        synchronized (PREF_CARRIER_CONFIG) {
+            return mReleaseTime;
+        }
+    }
+
+    /** get the config of gba service package name used for now*/
+    public String loadServicePackageName(int subId, SharedPreferences sharedPref) {
+        try {
+            return mTelephony.getBoundGbaService(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "fail to get package name due to " + e);
+        }
+        return sharedPref != null ? sharedPref.getString(KEY_SERVICE_PACKAGE, null) : null;
+    }
+
+    /** get the config of gba release time used for now */
+    public int loadReleaseTime(int subId, SharedPreferences sharedPref) {
+        try {
+            return mTelephony.getGbaReleaseTime(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "fail to get package name due to " + e);
+        }
+        return sharedPref != null ? sharedPref.getInt(KEY_RELEASE_TIME, 0) : 0;
+    }
+
+    /** update the config of test gba service */
+    public void updateServiceConfig(Context cxt, boolean success, int reason,
+                String key, String btId) {
+        new Thread(() -> {
+            synchronized (PREF_SERVICE_CONFIG) {
+                mIsAuthSuccess = success;
+                mFailReason = reason;
+                mGbaKey = key;
+                mBTid = btId;
+                SharedPreferences sharedPref = cxt.getSharedPreferences(
+                        PREF_SERVICE_CONFIG, Context.MODE_PRIVATE);
+                SharedPreferences.Editor editor = sharedPref.edit();
+                editor.putBoolean(KEY_AUTH_RESULT, success);
+                editor.putInt(KEY_FAIL_REASON, reason);
+                editor.putString(KEY_GBA_KEY, key);
+                editor.putString(KEY_B_TID, btId);
+                editor.commit();
+            }
+        }).start();
+    }
+
+    /** get the config of the authentication result */
+    public boolean getAuthResult() {
+        synchronized (PREF_SERVICE_CONFIG) {
+            return mIsAuthSuccess;
+        }
+    }
+
+    /** get the config of authentication fail cause */
+    public int getFailReason() {
+        synchronized (PREF_SERVICE_CONFIG) {
+            return mFailReason;
+        }
+    }
+
+    /** get the config of GBA key */
+    public String getGbaKey() {
+        synchronized (PREF_SERVICE_CONFIG) {
+            return mGbaKey;
+        }
+    }
+
+    /** get the config of B-Tid */
+    public String getBTid() {
+        synchronized (PREF_SERVICE_CONFIG) {
+            return mBTid;
+        }
+    }
+
+    /** update the config of the test */
+    public void updateTestConfig(Context cxt, int appType, String url,
+            boolean force, int org, int spId, int tlsCs) {
+        new Thread(() -> {
+            synchronized (PREF_TEST_CONFIG) {
+                mAppType = appType;
+                mNafUrl = url;
+                mForceBootstrap = force;
+                mOrg = org;
+                mSpId = spId;
+                mTlsCs = tlsCs;
+
+                SharedPreferences sharedPref = cxt.getSharedPreferences(
+                        PREF_TEST_CONFIG, Context.MODE_PRIVATE);
+                SharedPreferences.Editor editor = sharedPref.edit();
+                editor.putInt(KEY_APP_TYPE, appType);
+                editor.putString(KEY_NAF_URL, url);
+                editor.putBoolean(KEY_FORCE_BT, force);
+                editor.putInt(KEY_ORG, org);
+                editor.putInt(KEY_SP_ID, spId);
+                editor.putInt(KEY_TLS_CS, tlsCs);
+                editor.commit();
+            }
+        }).start();
+    }
+
+    /** get the config of the uicc application type*/
+    public int getAppType() {
+        synchronized (PREF_TEST_CONFIG) {
+            return mAppType;
+        }
+    }
+
+    /** get the config of NAF url */
+    public String getNafUrl() {
+        synchronized (PREF_TEST_CONFIG) {
+            return mNafUrl;
+        }
+    }
+
+    /** get the config if bootstrap is forced */
+    public boolean isForceBootstrap() {
+        synchronized (PREF_TEST_CONFIG) {
+            return mForceBootstrap;
+        }
+    }
+
+    /** get the config of the organization code */
+    public int getOrg() {
+        synchronized (PREF_TEST_CONFIG) {
+            return mOrg;
+        }
+    }
+
+    /** get the config of the security protocol id */
+    public int getSpId() {
+        synchronized (PREF_TEST_CONFIG) {
+            return mSpId;
+        }
+    }
+
+    /** get the config of the tls ciper suite id */
+    public int getTlsCs() {
+        synchronized (PREF_TEST_CONFIG) {
+            return mTlsCs;
+        }
+    }
+
+    /** convert byte arry to hex string */
+    public static String byteArrayToHexString(byte[] data) {
+        if (data == null || data.length == 0) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (byte b : data) {
+            sb.append(String.format(Locale.US, "%02X", b));
+        }
+        return sb.toString();
+    }
+}
diff --git a/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/TestGbaService.java b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/TestGbaService.java
new file mode 100644
index 0000000..4b0636c
--- /dev/null
+++ b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/TestGbaService.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 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.phone.testapps.gbatestapp;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.telephony.gba.GbaService;
+import android.util.Log;
+
+/** test GbaService to be used for Gba api test */
+public class TestGbaService extends GbaService {
+
+    private static final String TAG = "TestGbaService";
+
+    private Settings mSettings;
+
+    @Override
+    public void onCreate() {
+        Log.i(TAG, "TestGbaService: onCreate");
+        mSettings = Settings.getSettings(getApplicationContext());
+    }
+
+    @Override
+    public void onAuthenticationRequest(int subId, int token, int appType,
+            @NonNull Uri nafUrl, @NonNull byte[] securityProtocol, boolean forceBootStrapping) {
+        boolean isSuccess = mSettings.getAuthResult();
+        int reason = mSettings.getFailReason();
+        String key = mSettings.getGbaKey();
+        String btid = mSettings.getBTid();
+
+        if (isSuccess) {
+            reportKeysAvailable(token, key.getBytes(), btid);
+        } else {
+            reportAuthenticationFailure(token, reason);
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.d(TAG, "onBind intent:" + intent);
+        return super.onBind(intent);
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(TAG, "onDestroy!");
+        super.onDestroy();
+    }
+}
diff --git a/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/CarrierConfigFragment.java b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/CarrierConfigFragment.java
new file mode 100644
index 0000000..b0bfc32
--- /dev/null
+++ b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/CarrierConfigFragment.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2020 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.phone.testapps.gbatestapp.ui.main;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.phone.testapps.gbatestapp.R;
+import com.android.phone.testapps.gbatestapp.Settings;
+import com.android.phone.testapps.gbatestapp.TestGbaService;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link CarrierConfigFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class CarrierConfigFragment extends Fragment {
+    private static final String TAG = "CARRIER";
+
+    private static CarrierConfigFragment sInstance;
+
+    private Settings mSettings;
+    private EditText mEditPackageName;
+    private EditText mEditReleaseTime;
+
+    /** get the instance of CarrierConfigFragment */
+    public static CarrierConfigFragment newInstance() {
+        if (sInstance == null) {
+            Log.d(TAG, "new instance:");
+            sInstance = new CarrierConfigFragment();
+        }
+        return sInstance;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSettings = Settings.getSettings(getActivity());
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        View viewHierarchy = inflater.inflate(R.layout.fragment_carrier_config, container, false);
+        mEditPackageName = viewHierarchy.findViewById(R.id.editServicePackageName);
+        mEditReleaseTime = viewHierarchy.findViewById(R.id.editServiceReleaseTime);
+        getConfig(false);
+
+        Button buttonDone = viewHierarchy.findViewById(R.id.carrier_config_done);
+        buttonDone.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        mSettings.updateCarrierConfig(getActivity(),
+                                mEditPackageName.getText().toString(),
+                                Integer.parseInt(mEditReleaseTime.getText().toString()));
+                        getFragmentManager().beginTransaction().remove(
+                                CarrierConfigFragment.this).commitNow();
+                    }
+                }
+        );
+
+        Button buttonClear = viewHierarchy.findViewById(R.id.carrier_config_clear);
+        buttonClear.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        getConfig(true);
+                    }
+                }
+        );
+        return viewHierarchy;
+    }
+
+    private void getConfig(boolean isDefault) {
+        String packagename = mSettings.getServicePackageName();
+        if (isDefault || packagename == null) {
+            packagename = TestGbaService.class.getPackage().getName();
+        }
+        mEditPackageName.setText(packagename);
+        mEditReleaseTime.setText(isDefault ? "0" : Integer.toString(mSettings.getReleaseTime()));
+    }
+}
diff --git a/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/MainFragment.java b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/MainFragment.java
new file mode 100644
index 0000000..ff50f5c
--- /dev/null
+++ b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/MainFragment.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2020 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.phone.testapps.gbatestapp.ui.main;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.UaSecurityProtocolIdentifier;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.android.phone.testapps.gbatestapp.R;
+import com.android.phone.testapps.gbatestapp.Settings;
+
+/** main fragent to update settings and run the test */
+public class MainFragment extends Fragment {
+
+    private static final String TAG = "GBATestApp";
+    private static final String TAG_CARRIER = "carrier";
+    private static final String TAG_SERVICE = "service";
+    private static final String TAG_CLIENT = "client";
+
+    private static MainFragment sInstance;
+
+    private Settings mSettings;
+    private TelephonyManager mTelephonyManager;
+
+    /** Get the instance of MainFragment*/
+    public static MainFragment newInstance() {
+        if (sInstance == null) {
+            sInstance = new MainFragment();
+        }
+        return sInstance;
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        View viewHierarchy = inflater.inflate(R.layout.main_fragment, container, false);
+
+        Button buttonCarrier = viewHierarchy.findViewById(R.id.carrier_config_change_button);
+        buttonCarrier.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Fragment carrierFrag = getChildFragmentManager()
+                                .findFragmentByTag(TAG_CARRIER);
+                        if (carrierFrag == null) {
+                            carrierFrag = CarrierConfigFragment.newInstance();
+                        }
+                        getChildFragmentManager()
+                                .beginTransaction()
+                                .replace(R.id.main, carrierFrag, TAG_CARRIER)
+                                .commitNow();
+                    }
+                }
+        );
+
+        Button buttonService = viewHierarchy.findViewById(R.id.service_config);
+        buttonService.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Fragment serviceFrag = getChildFragmentManager()
+                                .findFragmentByTag(TAG_SERVICE);
+                        if (serviceFrag == null) {
+                            serviceFrag = ServiceConfigFragment.newInstance();
+                        }
+                        getChildFragmentManager()
+                                .beginTransaction()
+                                .replace(R.id.main, serviceFrag, TAG_SERVICE)
+                                .commitNow();
+                    }
+                }
+        );
+
+        Button buttonClient = viewHierarchy.findViewById(R.id.client_config);
+        buttonClient.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Fragment testFrag = getChildFragmentManager()
+                                .findFragmentByTag(TAG_CLIENT);
+                        if (testFrag == null) {
+                            testFrag = TestConfigFragment.newInstance();
+                        }
+
+                        getChildFragmentManager()
+                                .beginTransaction()
+                                .replace(R.id.main, testFrag, TAG_CLIENT)
+                                .commitNow();
+                    }
+                }
+        );
+
+        Button buttonExit = viewHierarchy.findViewById(R.id.test_exit);
+        buttonExit.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        getActivity().finish();
+                    }
+                }
+        );
+
+        Button buttonRunning = viewHierarchy.findViewById(R.id.send_request);
+        buttonRunning.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Log.d(TAG, "starting test...");
+                        TelephonyManager.BootstrapAuthenticationCallback cb = new
+                                TelephonyManager.BootstrapAuthenticationCallback() {
+                                    @Override
+                                    public void onKeysAvailable(byte[] gbaKey, String btId) {
+                                        String result = "onKeysAvailable, key:"
+                                                + Settings.byteArrayToHexString(gbaKey)
+                                                + ", btid:" + btId;
+                                        Log.d(TAG, result);
+                                        getActivity().runOnUiThread(()-> {
+                                            mTestLog.append(result + "\n");
+                                        });
+                                    }
+
+                                    @Override
+                                    public void onAuthenticationFailure(int reason) {
+                                        String result = "onAuthFailure, cause:" + reason;
+                                        Log.d(TAG, result);
+                                        getActivity().runOnUiThread(
+                                                () -> mTestLog.append(result + "\n"));
+                                    }
+                                };
+                        UaSecurityProtocolIdentifier.Builder builder =
+                                new UaSecurityProtocolIdentifier.Builder();
+                        try {
+                            if (mSettings.getOrg() != 0 || mSettings.getSpId() != 0
+                                    || mSettings.getTlsCs() != 0) {
+                                builder.setOrg(mSettings.getOrg())
+                                        .setProtocol(mSettings.getSpId())
+                                        .setTlsCipherSuite(mSettings.getTlsCs());
+                            }
+                        } catch (IllegalArgumentException e) {
+                            getActivity().runOnUiThread(() -> mTestLog.append(
+                                    "Fail to create UaSecurityProtocolIdentifier " + e + "\n"));
+                            return;
+                        }
+
+                        UaSecurityProtocolIdentifier spId = builder.build();
+                        Log.d(TAG, "bootstrapAuthenticationRequest with parameters [appType:"
+                                + mSettings.getAppType() + ", NAF:" + mSettings.getNafUrl()
+                                + ", spId:" + spId + ", isForceBootstrap:"
+                                + mSettings.isForceBootstrap() + "]");
+                        try {
+                            mTelephonyManager.bootstrapAuthenticationRequest(
+                                    mSettings.getAppType(), Uri.parse(mSettings.getNafUrl()),
+                                    spId, mSettings.isForceBootstrap(),
+                                    AsyncTask.SERIAL_EXECUTOR, cb);
+                        } catch (NullPointerException e) {
+                            getActivity().runOnUiThread(() -> mTestLog.append(
+                                    "Invalid parameters, please check!" + "\n"));
+                        }
+                    }
+                }
+        );
+
+        return viewHierarchy;
+    }
+
+    TextView mTestLog;
+    @Override
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mTelephonyManager = (TelephonyManager)
+                getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        mSettings = Settings.getSettings(getContext());
+        mTestLog = getActivity().findViewById(R.id.viewTestOutput);
+    }
+
+}
diff --git a/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/ServiceConfigFragment.java b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/ServiceConfigFragment.java
new file mode 100644
index 0000000..5e7f2fa
--- /dev/null
+++ b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/ServiceConfigFragment.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2020 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.phone.testapps.gbatestapp.ui.main;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.phone.testapps.gbatestapp.R;
+import com.android.phone.testapps.gbatestapp.Settings;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link ServiceConfigFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class ServiceConfigFragment extends Fragment {
+
+    private static final String TAG = "SERVICE";
+
+    private static ServiceConfigFragment sInstance;
+
+    private Settings mSettings;
+
+    private CheckBox mAuthResult;
+    private EditText mGbaKey;
+    private EditText mBTid;
+    private EditText mFailReason;
+
+    /** get the instance of ServiceConfigFragment */
+    public static ServiceConfigFragment newInstance() {
+        if (sInstance == null) {
+            sInstance = new ServiceConfigFragment();
+        }
+        return sInstance;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSettings = Settings.getSettings(getActivity());
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View  viewHierarchy = inflater.inflate(R.layout.fragment_service_config, container, false);
+
+        mAuthResult = viewHierarchy.findViewById(R.id.checkBoxResult);
+        mFailReason = viewHierarchy.findViewById(R.id.editFailReason);
+        mGbaKey = viewHierarchy.findViewById(R.id.editKey);
+        mBTid = viewHierarchy.findViewById(R.id.editBTid);
+
+        setDefault();
+
+        Button buttonDone = viewHierarchy.findViewById(R.id.service_config_done);
+        buttonDone.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        mSettings.updateServiceConfig(getActivity(), mAuthResult.isChecked(),
+                                Integer.parseInt(mFailReason.getText().toString()),
+                                mGbaKey.getText().toString(), mBTid.getText().toString());
+                        getFragmentManager().beginTransaction()
+                                .remove(ServiceConfigFragment.this).commitNow();
+                    }
+                }
+        );
+
+        Button buttonClear = viewHierarchy.findViewById(R.id.service_config_clear);
+        buttonClear.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        setDefault();
+                    }
+                }
+        );
+
+        return viewHierarchy;
+    }
+
+    private void setDefault() {
+        mAuthResult.setChecked(mSettings.getAuthResult());
+        String key = mSettings.getGbaKey();
+        if (key == null || key.isEmpty()) {
+            key = getString(R.string.sample_key);
+        }
+        mGbaKey.setText(key);
+        String id = mSettings.getBTid();
+        if (id == null || id.isEmpty()) {
+            id = getString(R.string.sample_btid);
+        }
+        mBTid.setText(id);
+        mFailReason.setText(Integer.toString(mSettings.getFailReason()));
+    }
+}
diff --git a/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/TestConfigFragment.java b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/TestConfigFragment.java
new file mode 100644
index 0000000..4049082
--- /dev/null
+++ b/testapps/GbaTestApp/src/com/android/phone/testapps/gbatestapp/ui/main/TestConfigFragment.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2020 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.phone.testapps.gbatestapp.ui.main;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.phone.testapps.gbatestapp.R;
+import com.android.phone.testapps.gbatestapp.Settings;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Use the {@link TestConfigFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class TestConfigFragment extends Fragment {
+
+    private static final String TAG = "TEST_CONFIG";
+
+    private static TestConfigFragment sInstance;
+
+    private Settings mSettings;
+
+    private EditText mAppType;
+    private EditText mUrl;
+    private EditText mOrg;
+    private EditText mSpId;
+    private EditText mTlsCs;
+    private CheckBox mForce;
+
+    /** get the instance of TestConfigFragment */
+    public static TestConfigFragment newInstance() {
+        if (sInstance == null) {
+            sInstance = new TestConfigFragment();
+        }
+        return sInstance;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSettings = Settings.getSettings(getActivity());
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View viewHierarchy = inflater.inflate(R.layout.fragment_test_config, container, false);
+        mAppType = viewHierarchy.findViewById(R.id.editAppType);
+        mUrl = viewHierarchy.findViewById(R.id.editUrl);
+        mOrg = viewHierarchy.findViewById(R.id.editOrg);
+        mSpId = viewHierarchy.findViewById(R.id.editSpId);
+        mTlsCs = viewHierarchy.findViewById(R.id.editTlsCs);
+        mForce = viewHierarchy.findViewById(R.id.checkBoxForce);
+
+        setDefault();
+
+        Button buttonDone = viewHierarchy.findViewById(R.id.client_config_done);
+        buttonDone.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Log.d(TAG, "updateTestConfig");
+                        mSettings.updateTestConfig(getActivity(),
+                                Integer.parseInt(mAppType.getText().toString()),
+                                mUrl.getText().toString(),
+                                mForce.isChecked(),
+                                Integer.parseInt(mOrg.getText().toString()),
+                                Integer.parseInt(mSpId.getText().toString()),
+                                Integer.parseInt(mTlsCs.getText().toString()));
+                        getFragmentManager().beginTransaction().remove(
+                                TestConfigFragment.this).commitNow();
+                    }
+                }
+        );
+
+        Button buttonClear = viewHierarchy.findViewById(R.id.client_config_clear);
+        buttonClear.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        setDefault();
+                    }
+                }
+        );
+
+        return viewHierarchy;
+    }
+
+    void setDefault() {
+        Log.d(TAG, "setDefault");
+        mAppType.setText(Integer.toString(mSettings.getAppType()));
+        String naf = mSettings.getNafUrl();
+        if (naf == null || naf.isEmpty()) {
+            naf = getString(R.string.sample_naf);
+        }
+        mUrl.setText(naf);
+        mOrg.setText(Integer.toString(mSettings.getOrg()));
+        mSpId.setText(Integer.toString(mSettings.getSpId()));
+        mTlsCs.setText(Integer.toString(mSettings.getTlsCs()));
+        mForce.setChecked(mSettings.isForceBootstrap());
+    }
+}