Merge "Fix handling of URIs with subId"
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 4e9b9d0..eebb681 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -1982,6 +1982,8 @@
         qb.setStrict(true); // a little protection from injection attacks
         qb.setTables(CARRIERS_TABLE);
 
+        List<String> constraints = new ArrayList<String>();
+
         int match = s_urlMatcher.match(url);
         switch (match) {
             case URL_TELEPHONY_USING_SUBID: {
@@ -1993,14 +1995,12 @@
                     return null;
                 }
                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
-                qb.appendWhere(NUMERIC + " = '" + mTelephonyManager.getSimOperator(subId) + "'");
-                // FIXME alter the selection to pass subId
-                // selection = selection + "and subId = "
+                constraints.add(NUMERIC + " = '" + mTelephonyManager.getSimOperator(subId) + "'");
+                constraints.add(SUBSCRIPTION_ID + "=" + subIdString);
             }
             // intentional fall through from above case
-            // do nothing
             case URL_TELEPHONY: {
-                qb.appendWhere(NOT_OWNED_BY_DPC);
+                constraints.add(NOT_OWNED_BY_DPC);
                 break;
             }
 
@@ -2013,21 +2013,20 @@
                     return null;
                 }
                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
-                // FIXME alter the selection to pass subId
-                // selection = selection + "and subId = "
+                constraints.add(SUBSCRIPTION_ID + "=" + subIdString);
             }
             //intentional fall through from above case
             case URL_CURRENT: {
-                qb.appendWhere("current IS NOT NULL");
-                qb.appendWhere(NOT_OWNED_BY_DPC);
+                constraints.add("current IS NOT NULL");
+                constraints.add(NOT_OWNED_BY_DPC);
                 // do not ignore the selection since MMS may use it.
                 //selection = null;
                 break;
             }
 
             case URL_ID: {
-                qb.appendWhere("_id = " + url.getPathSegments().get(1));
-                qb.appendWhere(NOT_OWNED_BY_DPC);
+                constraints.add("_id = " + url.getPathSegments().get(1));
+                constraints.add(NOT_OWNED_BY_DPC);
                 break;
             }
 
@@ -2041,11 +2040,12 @@
                     return null;
                 }
                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
+                constraints.add(SUBSCRIPTION_ID + "=" + subIdString);
             }
             //intentional fall through from above case
             case URL_PREFERAPN:
             case URL_PREFERAPN_NO_UPDATE: {
-                qb.appendWhere("_id = " + getPreferredApnId(subId, true));
+                constraints.add("_id = " + getPreferredApnId(subId, true));
                 break;
             }
 
@@ -2059,6 +2059,11 @@
             }
         }
 
+        // appendWhere doesn't add ANDs so we do it ourselves
+        if (constraints.size() > 0) {
+            qb.appendWhere(TextUtils.join(" AND ", constraints));
+        }
+
         if (match != URL_SIMINFO) {
             if (projectionIn != null) {
                 for (String column : projectionIn) {
@@ -2200,6 +2205,8 @@
                     values = new ContentValues();
                 }
 
+                values.put(SUBSCRIPTION_ID, subId);
+
                 values = DatabaseHelper.setDefaultValue(values);
                 if (!values.containsKey(EDITED)) {
                     values.put(EDITED, USER_EDITED);
diff --git a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
index e66dc09..74dfdf9 100644
--- a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
@@ -35,6 +35,7 @@
 import android.os.FileUtils;
 import android.provider.Telephony.Carriers;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
@@ -51,6 +52,10 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
 
 /**
  * Tests for testing CRUD operations of TelephonyProvider.
@@ -73,6 +78,12 @@
 
     private int notifyChangeCount;
 
+    private static final String TEST_SUBID = "1";
+    private static final String TEST_OPERATOR = "123456";
+    // Used to test the path for URL_TELEPHONY_USING_SUBID with subid 0
+    private static final Uri CONTENT_URI_WITH_SUBID = Uri.parse(
+            "content://telephony/carriers/subId/" + TEST_SUBID);
+
 
     /**
      * This is used to give the TelephonyProviderTest a mocked context which takes a
@@ -81,6 +92,7 @@
      */
     private class MockContextWithProvider extends MockContext {
         private final MockContentResolver mResolver;
+        private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
 
         public MockContextWithProvider(TelephonyProvider telephonyProvider) {
             mResolver = new MockContentResolver() {
@@ -91,6 +103,9 @@
                 }
             };
 
+            // return test subId 0 for all operators
+            doReturn(TEST_OPERATOR).when(mTelephonyManager).getSimOperator(anyInt());
+
             // Add authority="telephony" to given telephonyProvider
             ProviderInfo providerInfo = new ProviderInfo();
             providerInfo.authority = "telephony";
@@ -108,8 +123,13 @@
 
         @Override
         public Object getSystemService(String name) {
-            Log.d(TAG, "getSystemService: returning null");
-            return null;
+            if (name.equals(Context.TELEPHONY_SERVICE)) {
+                Log.d(TAG, "getSystemService: returning mock TM");
+                return mTelephonyManager;
+            } else {
+                Log.d(TAG, "getSystemService: returning null");
+                return null;
+            }
         }
 
         @Override
@@ -165,7 +185,7 @@
         final String insertApn = "exampleApnName";
         final String insertName = "exampleName";
         final Integer insertCurrent = 1;
-        final String insertNumeric = "123456";
+        final String insertNumeric = TEST_OPERATOR;
         contentValues.put(Carriers.APN, insertApn);
         contentValues.put(Carriers.NAME, insertName);
         contentValues.put(Carriers.CURRENT, insertCurrent);
@@ -222,19 +242,80 @@
     @Test
     @SmallTest
     public void testInsertCarriers() {
+        doSimpleTestForUri(Carriers.CONTENT_URI);
+    }
+
+    /**
+     * Test inserting, querying, and deleting values in carriers table.
+     * Verify that the inserted values match the result of the query and are deleted.
+     */
+    @Test
+    @SmallTest
+    public void testInsertCarriersWithSubId() {
+        doSimpleTestForUri(CONTENT_URI_WITH_SUBID);
+    }
+
+    private void doSimpleTestForUri(Uri uri) {
         // insert test contentValues
         ContentValues contentValues = new ContentValues();
         final String insertApn = "exampleApnName";
         final String insertName = "exampleName";
-        final Integer insertCurrent = 1;
-        final String insertNumeric = "123456";
-        final Integer insertOwnedby = Carriers.OWNED_BY_DPC;
-        final Integer expectedOwnedby = Carriers.OWNED_BY_OTHERS;
+        final String insertNumeric = TEST_OPERATOR;
         contentValues.put(Carriers.APN, insertApn);
         contentValues.put(Carriers.NAME, insertName);
-        contentValues.put(Carriers.CURRENT, insertCurrent);
         contentValues.put(Carriers.NUMERIC, insertNumeric);
-        contentValues.put(Carriers.OWNED_BY, insertOwnedby);
+
+        Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues);
+        mContentResolver.insert(uri, contentValues);
+
+        // get values in table
+        final String[] testProjection =
+        {
+            Carriers.APN,
+            Carriers.NAME,
+        };
+        final String selection = Carriers.NUMERIC + "=?";
+        String[] selectionArgs = { insertNumeric };
+        Log.d(TAG, "testInsertCarriers query projection: " + testProjection
+                + "\ntestInsertCarriers selection: " + selection
+                + "\ntestInsertCarriers selectionArgs: " + selectionArgs);
+        Cursor cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+
+        // verify that inserted values match results of query
+        assertNotNull(cursor);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+        final String resultApn = cursor.getString(0);
+        final String resultName = cursor.getString(1);
+        assertEquals(insertApn, resultApn);
+        assertEquals(insertName, resultName);
+
+        // delete test content
+        final String selectionToDelete = Carriers.NUMERIC + "=?";
+        String[] selectionArgsToDelete = { insertNumeric };
+        Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+                + "testInsertCarriers selectionArgs: " + selectionArgs);
+        int numRowsDeleted = mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
+        assertEquals(1, numRowsDeleted);
+
+        // verify that deleted values are gone
+        cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+        assertEquals(0, cursor.getCount());
+    }
+
+    @Test
+    @SmallTest
+    public void testOwnedBy() {
+        // insert test contentValues
+        ContentValues contentValues = new ContentValues();
+        final String insertApn = "exampleApnName";
+        final String insertName = "exampleName";
+        final String insertNumeric = TEST_OPERATOR;
+        final Integer insertOwnedBy = Carriers.OWNED_BY_OTHERS;
+        contentValues.put(Carriers.APN, insertApn);
+        contentValues.put(Carriers.NAME, insertName);
+        contentValues.put(Carriers.NUMERIC, insertNumeric);
+        contentValues.put(Carriers.OWNED_BY, insertOwnedBy);
 
         Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues);
         mContentResolver.insert(Carriers.CONTENT_URI, contentValues);
@@ -244,7 +325,6 @@
         {
             Carriers.APN,
             Carriers.NAME,
-            Carriers.CURRENT,
             Carriers.OWNED_BY,
         };
         final String selection = Carriers.NUMERIC + "=?";
@@ -261,13 +341,11 @@
         cursor.moveToFirst();
         final String resultApn = cursor.getString(0);
         final String resultName = cursor.getString(1);
-        final Integer resultCurrent = cursor.getInt(2);
-        final Integer resultOwnedby = cursor.getInt(3);
+        final Integer resultOwnedBy = cursor.getInt(2);
         assertEquals(insertApn, resultApn);
         assertEquals(insertName, resultName);
-        assertEquals(insertCurrent, resultCurrent);
-        // Verify that OWNED_BY is force set to OWNED_BY_OTHERS when inserted with general uri.
-        assertEquals(expectedOwnedby, resultOwnedby);
+        // Verify that OWNED_BY is force set to OWNED_BY_OTHERS when inserted with general uri
+        assertEquals(insertOwnedBy, resultOwnedBy);
 
         // delete test content
         final String selectionToDelete = Carriers.NUMERIC + "=?";
diff --git a/tests/src/com/android/providers/telephony/TelephonyProviderTestable.java b/tests/src/com/android/providers/telephony/TelephonyProviderTestable.java
index c3924c3..7ed40ae 100644
--- a/tests/src/com/android/providers/telephony/TelephonyProviderTestable.java
+++ b/tests/src/com/android/providers/telephony/TelephonyProviderTestable.java
@@ -19,6 +19,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.provider.Telephony;
+import android.support.test.InstrumentationRegistry;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -78,7 +79,7 @@
 
 
         public InMemoryTelephonyProviderDbHelper() {
-            super(null,      // no context is needed for in-memory db
+            super(InstrumentationRegistry.getTargetContext(),
                     null,    // db file name is null for in-memory db
                     null,    // CursorFactory is null by default
                     1);      // db version is no-op for tests