blob: 446c67c30c354ad38c53d772dda93288e605eeff [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.telecom.cts;
import static android.telecom.cts.TestUtils.TEST_PHONE_ACCOUNT_HANDLE;
import static android.telecom.cts.TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS;
import static com.android.compatibility.common.util.ShellIdentityUtils
.invokeMethodWithShellPermissionsNoReturn;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import android.app.AppOpsManager;
import android.app.UiModeManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.telecom.TelecomManager;
import android.telecom.cts.thirdptyincallservice.CtsThirdPartyInCallService;
import android.telecom.cts.thirdptyincallservice.CtsThirdPartyInCallServiceControl;
import android.telecom.cts.thirdptyincallservice.ICtsThirdPartyInCallServiceControl;
import android.text.TextUtils;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class ThirdPartyInCallServiceAppOpsPermissionTest extends BaseTelecomTestWithMockServices {
private static final String TAG = ThirdPartyInCallServiceAppOpsPermissionTest
.class.getSimpleName();
private static final String THIRD_PARITY_PACKAGE_NAME = CtsThirdPartyInCallService
.class.getPackage().getName();
private static final Uri TEST_URI = Uri.parse("tel:555-TEST");
private Context mContext;
private AppOpsManager mAppOpsManager;
private PackageManager mPackageManager;
private TelecomManager mTelecomManager;
ICtsThirdPartyInCallServiceControl mICtsThirdPartyInCallServiceControl;
private boolean mExpectedTearDownBindingStatus;
@Override
public void setUp() throws Exception {
super.setUp();
if (!mShouldTestTelecom) {
return;
}
mContext = getInstrumentation().getContext();
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mPackageManager = mContext.getPackageManager();
mTelecomManager = mContext.getSystemService(TelecomManager.class);
setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
setUpControl();
mICtsThirdPartyInCallServiceControl.resetLatchForServiceBound(false);
mExpectedTearDownBindingStatus = true;
}
@Override
public void tearDown() throws Exception {
if (!mShouldTestTelecom) {
return;
}
mICtsThirdPartyInCallServiceControl.resetCalls();
super.tearDown();
if (mExpectedTearDownBindingStatus) {
assertBindStatus(/* true: bind, false: unbind */false, /* expected result */true);
}
}
public void testWithoutAppOpsPermission() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setInCallServiceAppOpsPermission(false);
int previousCallCount = mICtsThirdPartyInCallServiceControl.getLocalCallCount();
addAndVerifyNewIncomingCall(TEST_URI, null);
assertBindStatus(/* true: bind, false: unbind */true, /* expected result */false);
assertCallCount(previousCallCount);
// Third Party InCallService hasn't been bound yet, unbound latch can be null when tearDown.
mExpectedTearDownBindingStatus = false;
}
public void testWithAppOpsPermission() throws Exception {
if (!mShouldTestTelecom) {
return;
}
// Grant App Ops Permission
setInCallServiceAppOpsPermission(true);
int previousCallCount = mICtsThirdPartyInCallServiceControl.getLocalCallCount();
addAndVerifyNewIncomingCall(TEST_URI, null);
assertBindStatus(/* true: bind, false: unbind */true, /* expected result */true);
assertCallCount(previousCallCount + 1);
mICtsThirdPartyInCallServiceControl.resetLatchForServiceBound(true);
// Revoke App Ops Permission
setInCallServiceAppOpsPermission(false);
}
/**
*
* @param bind: check the status of InCallService bind latches.
* Values: true (bound latch), false (unbound latch).
* @param success: whether the latch should have counted down.
*/
private void assertBindStatus(boolean bind, boolean success) {
waitUntilConditionIsTrueOrTimeout(new Condition() {
@Override
public Object expected() {
return success;
}
@Override
public Object actual() {
try {
return mICtsThirdPartyInCallServiceControl.checkBindStatus(bind);
} catch (RemoteException re) {
Log.e(TAG, "Remote exception when checking bind status: " + re);
return false;
}
}
}, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, "Unable to " + (bind ? "Bind" : "Unbind")
+ " third party in call service");
}
private void assertCallCount(int expected) {
waitUntilConditionIsTrueOrTimeout(new Condition() {
@Override
public Object expected() {
return expected;
}
@Override
public Object actual() {
try {
return mICtsThirdPartyInCallServiceControl.getLocalCallCount();
} catch (RemoteException re) {
Log.e(TAG, "Remote exception when getting local call count: " + re);
return -1;
}
}
}, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
"Failed to match localCallCount and expected: " + expected);
}
private void setUpControl() throws InterruptedException {
Intent bindIntent = new Intent(CtsThirdPartyInCallServiceControl.CONTROL_INTERFACE_ACTION);
// mContext is android.telecom.cts, which doesn't include thirdptyincallservice.
ComponentName controlComponentName =
ComponentName.createRelative(
CtsThirdPartyInCallServiceControl.class.getPackage().getName(),
CtsThirdPartyInCallServiceControl.class.getName());
bindIntent.setComponent(controlComponentName);
final CountDownLatch bindLatch = new CountDownLatch(1);
boolean success = mContext.bindService(bindIntent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "Service Connected: " + name);
mICtsThirdPartyInCallServiceControl =
ICtsThirdPartyInCallServiceControl.Stub.asInterface(service);
bindLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mICtsThirdPartyInCallServiceControl = null;
}
}, Context.BIND_AUTO_CREATE);
if (!success) {
fail("Failed to get control interface -- bind error");
}
bindLatch.await(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
private void setInCallServiceAppOpsPermission(boolean allow)
throws PackageManager.NameNotFoundException {
int uid = mPackageManager.getApplicationInfo(THIRD_PARITY_PACKAGE_NAME, 0).uid;
invokeMethodWithShellPermissionsNoReturn(mAppOpsManager,
(appOpsMan) -> appOpsMan.setUidMode(AppOpsManager.OPSTR_MANAGE_ONGOING_CALLS,
uid, allow ? AppOpsManager.MODE_ALLOWED : AppOpsManager.opToDefaultMode(
AppOpsManager.OPSTR_MANAGE_ONGOING_CALLS)));
}
}