cellbroadcast provider refactor

1. expose proper APIs to query framework cellbroadcast provider
2. export framework cellbroadcast provider
3. new URI with proper permission check

Bug: 135956699
Test: Build & Manual
Change-Id: I2cf0d0314e5f0283e6bde71407a1d43d7373a2e7
(cherry picked from commit fc0a165b8efc360c17fe22e94211038590a3f30c)
Merged-in: I2cf0d0314e5f0283e6bde71407a1d43d7373a2e7
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3b95bce..d5c379b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -108,7 +108,7 @@
                   android:writePermission="android.permission.MODIFY_PHONE_STATE" />
 
         <provider android:name="CellBroadcastProvider"
-                  android:authorities="cellbroadcasts_fwk"
+                  android:authorities="cellbroadcasts"
                   android:exported="true"
                   android:singleUser="true"
                   android:multiprocess="false" />
diff --git a/src/com/android/providers/telephony/CellBroadcastProvider.java b/src/com/android/providers/telephony/CellBroadcastProvider.java
index ccb3f4a..3c200bd 100644
--- a/src/com/android/providers/telephony/CellBroadcastProvider.java
+++ b/src/com/android/providers/telephony/CellBroadcastProvider.java
@@ -26,9 +26,11 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Process;
+import android.provider.Telephony;
 import android.provider.Telephony.CellBroadcasts;
 import android.text.TextUtils;
 import android.util.Log;
@@ -49,8 +51,14 @@
         /** Return {@code True} if the caller has the permission to write/update the database. */
         boolean hasWritePermission();
 
-        /** Return {@code True} if the caller has the permission to query the database. */
+        /** Return {@code True} if the caller has the permission to query the complete database. */
         boolean hasReadPermission();
+
+        /**
+         * Return {@code True} if the caller has the permission to query the database for
+         * cell broadcast message history.
+         */
+        boolean hasReadPermissionForHistory();
     }
 
     private static final String TAG = CellBroadcastProvider.class.getSimpleName();
@@ -69,6 +77,14 @@
     /** URI matcher type to get all cell broadcasts. */
     private static final int ALL = 0;
 
+    /**
+     * URI matcher type for get all message history, this is used primarily for default
+     * cellbroadcast app or messaging app to display message history. some information is not
+     * exposed for messaging history, e.g, messages which are out of broadcast geometrics will not
+     * be delivered to end users thus will not be returned as message history query result.
+     */
+    private static final int MESSAGE_HISTORY = 1;
+
     /** MIME type for the list of all cell broadcasts. */
     private static final String LIST_TYPE = "vnd.android.cursor.dir/cellbroadcast";
 
@@ -78,10 +94,10 @@
 
     /** Authority string for content URIs. */
     @VisibleForTesting
-    public static final String AUTHORITY = "cellbroadcasts_fwk";
+    public static final String AUTHORITY = "cellbroadcasts";
 
     /** Content uri of this provider. */
-    public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts_fwk");
+    public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
 
     @VisibleForTesting
     public PermissionChecker mPermissionChecker;
@@ -92,6 +108,7 @@
 
     static {
         sUriMatcher.addURI(AUTHORITY, null, ALL);
+        sUriMatcher.addURI(AUTHORITY, "history", MESSAGE_HISTORY);
     }
 
     public CellBroadcastProvider() {}
@@ -129,7 +146,7 @@
     @Override
     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
             String sortOrder) {
-        checkReadPermission();
+        checkReadPermission(uri);
 
         if (DBG) {
             Log.d(TAG, "query:"
@@ -139,6 +156,9 @@
                     + " selectionArgs = " + Arrays.toString(selectionArgs)
                     + " sortOrder = " + sortOrder);
         }
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setStrict(true); // a little protection from injection attacks
+        qb.setTables(CELL_BROADCASTS_TABLE_NAME);
 
         String orderBy;
         if (!TextUtils.isEmpty(sortOrder)) {
@@ -153,6 +173,11 @@
                 return getReadableDatabase().query(
                         CELL_BROADCASTS_TABLE_NAME, projection, selection, selectionArgs,
                         null /* groupBy */, null /* having */, orderBy);
+            case MESSAGE_HISTORY:
+                // limit projections to certain columns. limit result to broadcasted messages only.
+                qb.appendWhere(CellBroadcasts.MESSAGE_BROADCASTED  + "=1");
+                return qb.query(getReadableDatabase(), projection, selection, selectionArgs, null,
+                        null, orderBy);
             default:
                 throw new IllegalArgumentException(
                         "Query method doesn't support this uri = " + uri);
@@ -282,10 +307,24 @@
         }
     }
 
-    private void checkReadPermission() {
-        if (!mPermissionChecker.hasReadPermission()) {
-            throw new SecurityException(
-                    "No permission to read CellBroadcast provider");
+    private void checkReadPermission(Uri uri) {
+        int match = sUriMatcher.match(uri);
+        switch (match) {
+            case ALL:
+                if (!mPermissionChecker.hasReadPermission()) {
+                    throw new SecurityException(
+                            "No permission to read CellBroadcast provider");
+                }
+                break;
+            case MESSAGE_HISTORY:
+                // TODO: if we plan to allow apps to query db in framework, we should migrate data
+                // first before deprecating app's database. otherwise users will lose all history.
+                if (!mPermissionChecker.hasReadPermissionForHistory()) {
+                    throw new SecurityException(
+                            "No permission to read CellBroadcast provider for message history");
+                }
+            default:
+                return;
         }
     }
 
@@ -315,14 +354,28 @@
     private class CellBroadcastPermissionChecker implements PermissionChecker {
         @Override
         public boolean hasWritePermission() {
-            // Only the phone process has the write permission to modify this provider. 
-            return Binder.getCallingUid() == Process.PHONE_UID;
+            // Only the phone and network statck process has the write permission to modify this
+            // provider.
+            return Binder.getCallingUid() == Process.PHONE_UID
+                    || Binder.getCallingUid() == Process.NETWORK_STACK_UID;
         }
 
         @Override
         public boolean hasReadPermission() {
-            // Only the phone process has the read permission to query data from this provider. 
-            return Binder.getCallingUid() == Process.PHONE_UID;
+            // Only the phone and network stack process has the read permission to query data from
+            // this provider.
+            return Binder.getCallingUid() == Process.PHONE_UID
+                    || Binder.getCallingUid() == Process.NETWORK_STACK_UID;
+        }
+
+        @Override
+        public boolean hasReadPermissionForHistory() {
+            int status = getContext().checkCallingOrSelfPermission(
+                    "android.permission.RECEIVE_EMERGENCY_BROADCAST");
+            if (status == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+            return false;
         }
     }
 }
diff --git a/tests/src/com/android/providers/telephony/CellBroadcastProviderTest.java b/tests/src/com/android/providers/telephony/CellBroadcastProviderTest.java
index b5ff07b..bd92643 100644
--- a/tests/src/com/android/providers/telephony/CellBroadcastProviderTest.java
+++ b/tests/src/com/android/providers/telephony/CellBroadcastProviderTest.java
@@ -40,7 +40,7 @@
 public class CellBroadcastProviderTest extends TestCase {
     private static final String TAG = CellBroadcastProviderTest.class.getSimpleName();
 
-    public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts_fwk");
+    public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
 
     private static final int GEO_SCOPE = 1;
     private static final String PLMN = "123456";