Merge qt-r1-dev-plus-aosp-without-vendor (5817612) into stage-aosp-master

Bug: 135460123
Change-Id: I1037d2cd43cd2607de190bc020914c6fdc730b03
Merged-In: I73d74715a8dc817b6aacb7fac3460cad0054be3a
diff --git a/src/com/android/providers/tv/TvProvider.java b/src/com/android/providers/tv/TvProvider.java
index 76bdf8e..d57eb8b 100644
--- a/src/com/android/providers/tv/TvProvider.java
+++ b/src/com/android/providers/tv/TvProvider.java
@@ -124,7 +124,6 @@
     private static final String OP_UPDATE = "update";
     private static final String OP_DELETE = "delete";
 
-
     private static final UriMatcher sUriMatcher;
     private static final int MATCH_CHANNEL = 1;
     private static final int MATCH_CHANNEL_ID = 2;
@@ -1660,13 +1659,33 @@
         }
         Map<String, String> columnProjectionMap = new HashMap<>();
         for (String columnName : projection) {
-            // Value NULL will be provided if the requested column does not exist in the database.
-            columnProjectionMap.put(columnName,
-                     projectionMap.getOrDefault(columnName, "NULL as " + columnName));
+            String value = projectionMap.get(columnName);
+            if (value != null) {
+                columnProjectionMap.put(columnName, value);
+            } else {
+                // Value NULL will be provided if the requested column does not exist in the
+                // database.
+                value = "NULL AS " + DatabaseUtils.sqlEscapeString(columnName);
+                columnProjectionMap.put(columnName, value);
+
+                if (needEventLog(columnName)) {
+                    android.util.EventLog.writeEvent(0x534e4554, "135269669", -1, "");
+                }
+            }
         }
         return columnProjectionMap;
     }
 
+    private boolean needEventLog(String columnName) {
+        for (int i = 0; i < columnName.length(); i++) {
+            char c = columnName.charAt(i);
+            if (!Character.isLetterOrDigit(c) && c != '_') {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void filterContentValues(ContentValues values, Map<String, String> projectionMap) {
         Iterator<String> iter = values.keySet().iterator();
         while (iter.hasNext()) {
diff --git a/tests/src/com/android/providers/tv/TvProviderForTesting.java b/tests/src/com/android/providers/tv/TvProviderForTesting.java
index 0f9638e..5300756 100644
--- a/tests/src/com/android/providers/tv/TvProviderForTesting.java
+++ b/tests/src/com/android/providers/tv/TvProviderForTesting.java
@@ -21,6 +21,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.media.tv.TvContract;
 import android.net.Uri;
+import java.io.File;
 
 class TvProviderForTesting extends TvProvider {
     private static final String FAKE_SESSION_TOKEN = "TvProviderForTesting";
@@ -51,7 +52,10 @@
         super.shutdown();
 
         if (mDatabaseHelper != null) {
+            SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
+            File databaseFile = new File(db.getPath());
             mDatabaseHelper.close();
+            SQLiteDatabase.deleteDatabase(databaseFile);
         }
     }
 
diff --git a/tests/src/com/android/providers/tv/UnrecognizedColumnsTest.java b/tests/src/com/android/providers/tv/UnrecognizedColumnsTest.java
new file mode 100644
index 0000000..f552013
--- /dev/null
+++ b/tests/src/com/android/providers/tv/UnrecognizedColumnsTest.java
@@ -0,0 +1,116 @@
+package com.android.providers.tv;
+
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.media.tv.TvContract;
+import android.media.tv.TvContract.Programs;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import com.android.providers.tv.Utils.Program;
+import java.util.Arrays;
+
+import com.google.android.collect.Sets;
+
+public class UnrecognizedColumnsTest extends AndroidTestCase {
+    private static final String PERMISSION_ACCESS_ALL_EPG_DATA =
+            "com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA";
+    private static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS";
+
+    private static final String MY_PACKAGE = "example.my";
+    private static final String ANOTHER_PACKAGE = "example.another";
+
+    private MockContentResolver mResolver;
+    private TvProviderForTesting mProvider;
+    private MockTvProviderContext mContext;
+    private Program mProgram;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mResolver = new MockContentResolver();
+        mResolver.addProvider(Settings.AUTHORITY, new MockContentProvider() {
+            @Override
+            public Bundle call(String method, String request, Bundle args) {
+                return new Bundle();
+            }
+        });
+
+        mProvider = new TvProviderForTesting();
+        mResolver.addProvider(TvContract.AUTHORITY, mProvider);
+
+        mContext = new MockTvProviderContext(mResolver, getContext());
+        // get data of the calling package only
+        mContext.grantOrRejectPermission(PERMISSION_ACCESS_ALL_EPG_DATA, false);
+        mContext.grantOrRejectPermission(PERMISSION_READ_TV_LISTINGS, false);
+
+        setContext(mContext);
+
+        final ProviderInfo info = new ProviderInfo();
+        info.authority = TvContract.AUTHORITY;
+        mProvider.attachInfoForTesting(getContext(), info);
+    }
+
+
+    @Override
+    protected void tearDown() throws Exception {
+        Utils.clearTvProvider(mResolver);
+        mProvider.setOpenHelper(null, true);
+        mProvider.shutdown();
+        super.tearDown();
+    }
+
+    public void testUnrecognizedColumns() {
+        insertPrograms();
+
+        String[] projection = new String[] {
+            TvContract.Programs._ID,
+            "_random_name",
+            " with spaces ",
+            "\' in single quotes \'",
+            "\" in double quotes \"",
+            "quotes \' inside \' this \" name \"",
+        };
+
+        Cursor cursor =
+            mResolver.query(TvContract.Programs.CONTENT_URI, projection, null, null, null);
+        assertNotNull(cursor);
+        cursor.moveToNext();
+        assertEquals(1, cursor.getCount());
+
+        assertEquals(
+            "Column names don't match.",
+            Arrays.asList(
+                    Programs._ID,
+                    "_random_name",
+                    " with spaces ",
+                    "\' in single quotes \'",
+                    "\" in double quotes \"",
+                    "quotes \' inside \' this \" name \""),
+            Arrays.asList(cursor.getColumnNames()));
+
+        assertEquals(mProgram.id, cursor.getLong(0));
+        assertNull(cursor.getString(1));
+        assertNull(cursor.getString(2));
+        assertNull(cursor.getString(3));
+        assertNull(cursor.getString(4));
+        assertNull(cursor.getString(5));
+    }
+
+    private void insertPrograms() {
+        mProvider.callingPackage = MY_PACKAGE;
+        long myChannelId = Utils.insertChannel(mResolver);
+        mProgram = new Program(1, MY_PACKAGE);
+        Utils.insertPrograms(mResolver, myChannelId, mProgram);
+
+        mProvider.callingPackage = ANOTHER_PACKAGE;
+        long anotherChannelId = Utils.insertChannel(mResolver);
+        Program anotherProgram = new Program(2, ANOTHER_PACKAGE);
+        Utils.insertPrograms(mResolver, anotherChannelId, anotherProgram);
+
+        mProvider.callingPackage = MY_PACKAGE;
+    }
+}