Run tests that rely on BlockedNumberProvider from secondary users.

The BlockedNumberProvider can only be accesses from the primary user. In
order to run tests on a secondary user, a singleton service is created
and invoked from the telecom ad telephony tests that use the
BlockedNumberProvider.

Test: cts-tradefed run cts-dev -m CtsTelephonyTestCases
... on prod pi device running primary and secondary user. See bug for
additional details.
bug: 121246456
Change-Id: Ieb3af346e2b0d15257abf6d60630db777964a3cc
Merged-In: Ieb3af346e2b0d15257abf6d60630db777964a3cc
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java
new file mode 100644
index 0000000..360c078
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 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.compatibility.common.util;
+
+import static android.provider.BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER;
+import static android.provider.BlockedNumberContract.BlockedNumbers.CONTENT_URI;
+
+import android.app.IntentService;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * A service to handle interactions with the BlockedNumberProvider. The BlockedNumberProvider
+ * can only be accessed by the primary user. This service can be run as a singleton service
+ * which will then be able to access the BlockedNumberProvider from a test running in a
+ * secondary user.
+ */
+public class BlockedNumberService extends IntentService {
+
+    static final String INSERT_ACTION = "android.telecom.cts.InsertBlockedNumber";
+    static final String DELETE_ACTION = "android.telecom.cts.DeleteBlockedNumber";
+    static final String PHONE_NUMBER_EXTRA = "number";
+    static final String URI_EXTRA = "uri";
+    static final String ROWS_EXTRA = "rows";
+    static final String RESULT_RECEIVER_EXTRA = "resultReceiver";
+
+    private static final String TAG = "CtsBlockNumberSvc";
+
+    private ContentResolver mContentResolver;
+
+    public BlockedNumberService() {
+        super(BlockedNumberService.class.getName());
+    }
+
+    @Override
+    public void onHandleIntent(Intent intent) {
+        Log.i(TAG, "Starting BlockedNumberService service: " + intent);
+        if (intent == null) {
+            return;
+        }
+        Bundle bundle;
+        mContentResolver = getContentResolver();
+        switch (intent.getAction()) {
+            case INSERT_ACTION:
+                bundle = insertBlockedNumber(intent.getStringExtra(PHONE_NUMBER_EXTRA));
+                break;
+            case DELETE_ACTION:
+                bundle = deleteBlockedNumber(Uri.parse(intent.getStringExtra(URI_EXTRA)));
+                break;
+            default:
+                bundle = new Bundle();
+                break;
+        }
+        ResultReceiver receiver = intent.getParcelableExtra(RESULT_RECEIVER_EXTRA);
+        receiver.send(0, bundle);
+    }
+
+    private Bundle insertBlockedNumber(String number) {
+        Log.i(TAG, "insertBlockedNumber: " + number);
+
+        ContentValues cv = new ContentValues();
+        cv.put(COLUMN_ORIGINAL_NUMBER, number);
+        Uri uri = mContentResolver.insert(CONTENT_URI, cv);
+        Bundle bundle = new Bundle();
+        bundle.putString(URI_EXTRA, uri.toString());
+        return bundle;
+    }
+
+    private Bundle deleteBlockedNumber(Uri uri) {
+        Log.i(TAG, "deleteBlockedNumber: " + uri);
+
+        int rows = mContentResolver.delete(uri, null, null);
+        Bundle bundle = new Bundle();
+        bundle.putInt(ROWS_EXTRA, rows);
+        return bundle;
+    }
+}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java
new file mode 100644
index 0000000..e5a0ce4
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 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.compatibility.common.util;
+
+import static com.android.compatibility.common.util.BlockedNumberService.DELETE_ACTION;
+import static com.android.compatibility.common.util.BlockedNumberService.INSERT_ACTION;
+import static com.android.compatibility.common.util.BlockedNumberService.PHONE_NUMBER_EXTRA;
+import static com.android.compatibility.common.util.BlockedNumberService.RESULT_RECEIVER_EXTRA;
+import static com.android.compatibility.common.util.BlockedNumberService.ROWS_EXTRA;
+import static com.android.compatibility.common.util.BlockedNumberService.URI_EXTRA;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+
+import junit.framework.TestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility for starting the blocked number service.
+ */
+public class BlockedNumberUtil {
+
+    private static final int TIMEOUT = 2;
+
+    private BlockedNumberUtil() {}
+
+    /** Insert a phone number into the blocked number provider and returns the resulting Uri. */
+    public static Uri insertBlockedNumber(Context context, String phoneNumber) {
+        Intent intent = new Intent(INSERT_ACTION);
+        intent.putExtra(PHONE_NUMBER_EXTRA, phoneNumber);
+
+        return Uri.parse(runBlockedNumberService(context, intent).getString(URI_EXTRA));
+    }
+
+    /** Remove a number from the blocked number provider and returns the number of rows deleted. */
+    public static int deleteBlockedNumber(Context context, Uri uri) {
+        Intent intent = new Intent(DELETE_ACTION);
+        intent.putExtra(URI_EXTRA, uri.toString());
+
+        return runBlockedNumberService(context, intent).getInt(ROWS_EXTRA);
+    }
+
+    /** Start the blocked number service. */
+    static Bundle runBlockedNumberService(Context context, Intent intent) {
+        // Temporarily allow background service
+        SystemUtil.runShellCommand("cmd deviceidle tempwhitelist " + context.getPackageName());
+
+        final Semaphore semaphore = new Semaphore(0);
+        final Bundle result = new Bundle();
+
+        ResultReceiver receiver = new ResultReceiver(new Handler(Looper.getMainLooper())) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                result.putAll(resultData);
+                semaphore.release();
+            }
+        };
+        intent.putExtra(RESULT_RECEIVER_EXTRA, receiver);
+        intent.setComponent(new ComponentName(context, BlockedNumberService.class));
+
+        context.startService(intent);
+
+        try {
+            TestCase.assertTrue(semaphore.tryAcquire(TIMEOUT, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            TestCase.fail("Timed out waiting for result from BlockedNumberService");
+        }
+        return result;
+    }
+}
diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml
index cce9785..6d2ed72 100644
--- a/tests/tests/telecom/AndroidManifest.xml
+++ b/tests/tests/telecom/AndroidManifest.xml
@@ -71,6 +71,15 @@
             </intent-filter>
         </service>
 
+        <service android:name="com.android.compatibility.common.util.BlockedNumberService"
+            android:exported="true"
+            android:singleUser="true" >
+            <intent-filter>
+                <action android:name="android.telecom.cts.InsertBlockedNumber"/>
+                <action android:name="android.telecom.cts.DeleteBlockedNumber"/>
+            </intent-filter>
+        </service>
+
         <receiver android:name="android.telecom.cts.MockMissedCallNotificationReceiver">
             <intent-filter>
                 <action android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index 871f3e4..954112b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -17,13 +17,14 @@
 package android.telecom.cts;
 
 import static android.telecom.cts.TestUtils.*;
+import static com.android.compatibility.common.util.BlockedNumberUtil.deleteBlockedNumber;
+import static com.android.compatibility.common.util.BlockedNumberUtil.insertBlockedNumber;
 
 import android.app.UiModeManager;
 import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.BlockedNumberContract;
 import android.telecom.CallAudioState;
 import android.telecom.Call;
 import android.telecom.Connection;
@@ -393,17 +394,17 @@
             assertNull(mInCallCallbacks.getService());
         } finally {
             if (blockedUri != null) {
-                mContext.getContentResolver().delete(blockedUri, null, null);
+                unblockNumber(blockedUri);
             }
         }
     }
 
     private Uri blockNumber(Uri phoneNumberUri) {
-        ContentValues cv = new ContentValues();
-        cv.put(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
-                phoneNumberUri.getSchemeSpecificPart());
-        return mContext.getContentResolver().insert(
-                BlockedNumberContract.BlockedNumbers.CONTENT_URI, cv);
+        return insertBlockedNumber(mContext, phoneNumberUri.getSchemeSpecificPart());
+    }
+
+    private int unblockNumber(Uri uri) {
+        return deleteBlockedNumber(mContext, uri);
     }
 
     public void testAnswerIncomingCallAsVideo_SendsCorrectVideoState() {
diff --git a/tests/tests/telephony/AndroidManifest.xml b/tests/tests/telephony/AndroidManifest.xml
index b6840a3..2360145 100644
--- a/tests/tests/telephony/AndroidManifest.xml
+++ b/tests/tests/telephony/AndroidManifest.xml
@@ -30,6 +30,7 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.telephony.embms.cts.permission.TEST_BROADCAST"/>
 
     <permission android:name="android.telephony.embms.cts.permission.TEST_BROADCAST"
@@ -119,6 +120,15 @@
 
         </service>
 
+        <service android:name="com.android.compatibility.common.util.BlockedNumberService"
+                android:exported="true"
+                android:singleUser="true" >
+            <intent-filter>
+                <action android:name="android.telecom.cts.InsertBlockedNumber"/>
+                <action android:name="android.telecom.cts.DeleteBlockedNumber"/>
+            </intent-filter>
+        </service>
+
         <activity android:name="android.telephony.cts.StubDialerActvity">
             <intent-filter>
                 <action android:name="android.intent.action.DIAL"/>
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 74f9d13..89814aa 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -16,11 +16,12 @@
 
 package android.telephony.cts;
 
+import static com.android.compatibility.common.util.BlockedNumberUtil.deleteBlockedNumber;
+import static com.android.compatibility.common.util.BlockedNumberUtil.insertBlockedNumber;
 
 import android.app.PendingIntent;
 import android.app.UiAutomation;
 import android.content.BroadcastReceiver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -29,7 +30,6 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
-import android.provider.BlockedNumberContract;
 import android.provider.Telephony;
 import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
@@ -118,7 +118,7 @@
     @Override
     protected void tearDown() throws Exception {
         if (mBlockedNumberUri != null) {
-            mContext.getContentResolver().delete(mBlockedNumberUri, null, null);
+            unblockNumber(mBlockedNumberUri);
             mBlockedNumberUri = null;
         }
         if (mTestAppSetAsDefaultSmsApp) {
@@ -398,17 +398,18 @@
         getSmsManager().sendTextMessage(destAddr, null, text, sentIntent, deliveredIntent);
     }
 
-    private void blockNumber(String phoneNumber) {
-        ContentValues cv = new ContentValues();
-        cv.put(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER, phoneNumber);
-        mBlockedNumberUri = mContext.getContentResolver().insert(
-                BlockedNumberContract.BlockedNumbers.CONTENT_URI, cv);
+    private void blockNumber(String number) {
+        mBlockedNumberUri = insertBlockedNumber(mContext, number);
+    }
+
+    private void unblockNumber(Uri uri) {
+        deleteBlockedNumber(mContext, uri);
     }
 
     private void setDefaultSmsApp(boolean setToSmsApp)
             throws Exception {
         String command = String.format(
-                "appops set %s WRITE_SMS %s",
+                "appops set --user 0 %s WRITE_SMS %s",
                 mContext.getPackageName(),
                 setToSmsApp ? "allow" : "default");
         assertTrue("Setting default SMS app failed : " + setToSmsApp,