blob: 985aca935a26e2428b4cf1bd93cdf052c3fa481b [file] [log] [blame]
/*
* 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.
*/
package android.telephony.gba.cts;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import android.app.Instrumentation;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.cts.TelephonyUtils;
import android.telephony.gba.UaSecurityProtocolIdentifier;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.compatibility.common.util.ShellIdentityUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
@RunWith(AndroidJUnit4.class)
public final class GbaServiceTest {
private static final String TAG = "GbaServiceTest";
private static final String COMMAND_UPDATE_PACKAGE = "cmd phone gba set-service ";
private static final String COMMAND_UPDATE_RELEASE = "cmd phone gba set-release ";
private static final String COMMAND_GET_PACKAGE = "cmd phone gba get-service";
private static final String COMMAND_GET_RELEASE = "cmd phone gba get-release";
private static final String SERVICE_PACKAGE = "android.telephony.cts";
private static final String NAF = "3GPP-bootstrapping@naf1.operator.com";
private static final String BTID = "(B-TID)";
private static final int REQ_TIMEOUT = 5000;
private static final int RELEASE_DEFAULT = 0;
private static final int RELEASE_TEST_MS = 15 * 1000;
private static final int RELEASE_NEVER = -1;
private static int sSubId;
private static Instrumentation sInstrumentation;
private static TelephonyManager sTm;
private static TestGbaConfig sConfig;
private static String sServiceConfig;
private static int sReleaseTimeConfig;
@BeforeClass
public static void init() throws Exception {
if (!isFeatureSupported()) {
return;
}
sConfig = TestGbaConfig.getInstance();
sInstrumentation = InstrumentationRegistry.getInstrumentation();
sTm = sInstrumentation.getContext().getSystemService(TelephonyManager.class);
sServiceConfig = TelephonyUtils.executeShellCommand(
sInstrumentation, COMMAND_GET_PACKAGE);
sReleaseTimeConfig = Integer.parseInt(TelephonyUtils.executeShellCommand(
sInstrumentation, COMMAND_GET_RELEASE));
}
@AfterClass
public static void release() throws Exception {
if (!isFeatureSupported()) {
return;
}
setService(sServiceConfig);
setReleaseTime(sReleaseTimeConfig);
}
@Before
public void setUp() throws Exception {
assumeTrue(isFeatureSupported());
setService(SERVICE_PACKAGE);
setReleaseTime(RELEASE_DEFAULT);
}
@Test (expected = SecurityException.class)
public void testPermissions() {
runGbaFailCase(TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED,
android.Manifest.permission.READ_PHONE_STATE);
}
@Test
public void testAuthSuccess() {
Random rand = new Random();
for (int i = 0; i < 20; i++) {
Log.d(TAG, "testAuthSuccess[" + i + "]");
byte[] key = new byte[16];
rand.nextBytes(key);
sConfig.setConfig(true, key, BTID, TelephonyManager.GBA_FAILURE_REASON_UNKNOWN);
final AtomicBoolean isSuccess = new AtomicBoolean(false);
final AtomicBoolean isFail = new AtomicBoolean(false);
TelephonyManager.BootstrapAuthenticationCallback cb = new
TelephonyManager.BootstrapAuthenticationCallback() {
@Override
public void onKeysAvailable(byte[] gbaKey, String btId) {
super.onKeysAvailable(gbaKey, btId);
assertNotNull(gbaKey);
assertNotNull(btId);
assertArrayEquals(key, gbaKey);
assertEquals(BTID, btId);
synchronized (isSuccess) {
isSuccess.set(true);
isSuccess.notify();
}
}
@Override
public void onAuthenticationFailure(int reason) {
super.onAuthenticationFailure(reason);
synchronized (isSuccess) {
isFail.set(true);
isSuccess.notify();
}
}
};
UaSecurityProtocolIdentifier.Builder builder =
new UaSecurityProtocolIdentifier.Builder();
builder.setOrg(UaSecurityProtocolIdentifier.ORG_3GPP).setProtocol(
UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTm,
(tm) -> tm.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_USIM,
Uri.parse(NAF), builder.build(), true, AsyncTask.SERIAL_EXECUTOR, cb),
android.Manifest.permission.MODIFY_PHONE_STATE);
waitForMs(isSuccess, REQ_TIMEOUT);
assertTrue(isSuccess.get());
assertFalse(isFail.get());
}
}
@Test
public void testGbaNotSupported() throws Exception {
setService("");
sConfig.setConfig(true, new byte[16], BTID, TelephonyManager.GBA_FAILURE_REASON_UNKNOWN);
runGbaFailCase(TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED,
android.Manifest.permission.MODIFY_PHONE_STATE);
assertTrue(setService(SERVICE_PACKAGE));
}
@Test
public void testAuthFail() {
for (int r = TelephonyManager.GBA_FAILURE_REASON_UNKNOWN;
r <= TelephonyManager.GBA_FAILURE_REASON_SECURITY_PROTOCOL_NOT_SUPPORTED; r++) {
sConfig.setConfig(false, new byte[16], BTID, r);
runGbaFailCase(r, android.Manifest.permission.MODIFY_PHONE_STATE);
}
}
private void runGbaFailCase(int r, String permission) {
final AtomicBoolean isSuccess = new AtomicBoolean(false);
final AtomicBoolean isFail = new AtomicBoolean(false);
TelephonyManager.BootstrapAuthenticationCallback cb = new
TelephonyManager.BootstrapAuthenticationCallback() {
@Override
public void onKeysAvailable(byte[] gbaKey, String btId) {
synchronized (isFail) {
isSuccess.set(true);
isFail.notify();
}
}
@Override
public void onAuthenticationFailure(int reason) {
assertEquals(reason, r);
synchronized (isFail) {
isFail.set(true);
isFail.notify();
}
}
};
UaSecurityProtocolIdentifier.Builder builder = new UaSecurityProtocolIdentifier.Builder();
builder.setOrg(UaSecurityProtocolIdentifier.ORG_3GPP).setProtocol(
UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTm,
(tm) -> tm.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_USIM,
Uri.parse(NAF), builder.build(), true, AsyncTask.SERIAL_EXECUTOR, cb), permission);
waitForMs(isFail, REQ_TIMEOUT);
assertTrue(isFail.get());
assertFalse(isSuccess.get());
}
@Test
public void testServiceReleaseDefault() throws Exception {
int ss = sConfig.getServiceState();
boolean isExpected = (ss == TestGbaConfig.STATE_UNKNOWN
|| ss == TestGbaConfig.STATE_REMOVED
|| ss == TestGbaConfig.STATE_UNBOUND);
assertTrue(isExpected);
sConfig.setConfig(false, new byte[16], BTID,
TelephonyManager.GBA_FAILURE_REASON_UNKNOWN);
runGbaFailCase(TelephonyManager.GBA_FAILURE_REASON_UNKNOWN,
android.Manifest.permission.MODIFY_PHONE_STATE);
waitForMs(sConfig, 500);
assertFalse(TestGbaConfig.STATE_BOUND == sConfig.getServiceState());
}
@Test
public void testServiceReleaseDuration() throws Exception {
int ss = sConfig.getServiceState();
boolean isExpected = (ss == TestGbaConfig.STATE_UNKNOWN
|| ss == TestGbaConfig.STATE_REMOVED
|| ss == TestGbaConfig.STATE_UNBOUND);
assertTrue(isExpected);
sConfig.setConfig(false, new byte[16], BTID,
TelephonyManager.GBA_FAILURE_REASON_UNKNOWN);
assertTrue(setReleaseTime(RELEASE_TEST_MS));
runGbaFailCase(TelephonyManager.GBA_FAILURE_REASON_UNKNOWN,
android.Manifest.permission.MODIFY_PHONE_STATE);
waitForMs(sConfig, 500);
assertEquals(TestGbaConfig.STATE_BOUND, sConfig.getServiceState());
waitForMs(sConfig, RELEASE_TEST_MS);
assertFalse(TestGbaConfig.STATE_BOUND == sConfig.getServiceState());
}
@Test
public void testServiceNoRelease() throws Exception {
int ss = sConfig.getServiceState();
boolean isExpected = (ss == TestGbaConfig.STATE_UNKNOWN
|| ss == TestGbaConfig.STATE_REMOVED
|| ss == TestGbaConfig.STATE_UNBOUND);
assertTrue(isExpected);
sConfig.setConfig(false, new byte[16], BTID,
TelephonyManager.GBA_FAILURE_REASON_UNKNOWN);
assertTrue(setReleaseTime(RELEASE_NEVER));
runGbaFailCase(TelephonyManager.GBA_FAILURE_REASON_UNKNOWN,
android.Manifest.permission.MODIFY_PHONE_STATE);
waitForMs(sConfig, 2 * RELEASE_TEST_MS);
assertEquals(TestGbaConfig.STATE_BOUND, sConfig.getServiceState());
}
public static void waitForMs(Object obj, long ms) {
synchronized (obj) {
try {
obj.wait(ms);
} catch (InterruptedException e) {
Log.d(TAG, "InterruptedException while waiting: " + e);
}
}
}
private static boolean setService(String packageName) throws Exception {
String result = TelephonyUtils.executeShellCommand(sInstrumentation,
COMMAND_UPDATE_PACKAGE + packageName);
return "true".equals(result);
}
private static boolean setReleaseTime(int interval) throws Exception {
String result = TelephonyUtils.executeShellCommand(sInstrumentation,
COMMAND_UPDATE_RELEASE + interval);
return "true".equals(result);
}
private static boolean isFeatureSupported() {
if (!InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)) {
return false;
}
SubscriptionManager subscriptionManager = InstrumentationRegistry.getContext()
.getSystemService(SubscriptionManager.class);
int[] activeSubs = ShellIdentityUtils.invokeMethodWithShellPermissions(subscriptionManager,
(sm) -> sm.getActiveSubscriptionIdList());
if (activeSubs.length == 0) {
return false;
}
sSubId = activeSubs[0];
return true;
}
}