Support the new ACTION_EXTERNAL_PROVIDER_CHANGE intent

When a change is made to the SmsProvider or MmsProvider by a process
other than the default Sms app, notify the default Sms app of the change
so it can resync.

Change-Id: Ia3885ead436bfaf46c4a61a40e7435172060c785
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 80f0a41..49c71eb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4,9 +4,9 @@
      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.
@@ -24,6 +24,9 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
+    <!-- This permission is only used to send the ACTION_EXTERNAL_PROVIDER_CHANGE intent. -->
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+
     <application android:process="com.android.phone"
                  android:allowClearUserData="false"
                  android:allowBackup="false"
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index bd31cc1..515ecad 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -547,7 +547,7 @@
         }
 
         if (notify) {
-            notifyChange();
+            notifyChange(res);
         }
         return res;
     }
@@ -643,7 +643,7 @@
         }
 
         if ((deletedRows > 0) && notify) {
-            notifyChange();
+            notifyChange(uri);
         }
         return deletedRows;
     }
@@ -816,7 +816,7 @@
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         int count = db.update(table, finalValues, finalSelection, selectionArgs);
         if (notify && (count > 0)) {
-            notifyChange();
+            notifyChange(uri);
         }
         return count;
     }
@@ -922,9 +922,11 @@
         values.remove(Mms._ID);
     }
 
-    private void notifyChange() {
-        getContext().getContentResolver().notifyChange(
+    private void notifyChange(final Uri uri) {
+        final Context context = getContext();
+        context.getContentResolver().notifyChange(
                 MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
+        ProviderUtil.notifyIfNotDefaultSmsApp(uri, getCallingPackage(), context);
     }
 
     private final static String TAG = "MmsProvider";
diff --git a/src/com/android/providers/telephony/ProviderUtil.java b/src/com/android/providers/telephony/ProviderUtil.java
index 9435409..34c6da9 100644
--- a/src/com/android/providers/telephony/ProviderUtil.java
+++ b/src/com/android/providers/telephony/ProviderUtil.java
@@ -16,10 +16,16 @@
 
 package com.android.providers.telephony;
 
+import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Process;
 import android.provider.Telephony;
+import android.provider.Telephony.Sms;
+import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.telephony.SmsApplication;
 
@@ -27,6 +33,7 @@
  * Helpers
  */
 public class ProviderUtil {
+    private final static String TAG = "SmsProvider";
 
     /**
      * Check if a caller of the provider has restricted access,
@@ -68,4 +75,41 @@
                 (values.containsKey(Telephony.Sms.CREATOR) ||
                         values.containsKey(Telephony.Mms.CREATOR));
     }
+
+    /**
+     * Notify the default SMS app of an SMS/MMS provider change if the change is being made
+     * by a package other than the default SMS app itself.
+     *
+     * @param uri The uri the provider change applies to
+     * @param callingPackage The package name of the provider caller
+     * @param Context
+     */
+    public static void notifyIfNotDefaultSmsApp(final Uri uri, final String callingPackage,
+            final Context context) {
+        if (TextUtils.equals(callingPackage, Telephony.Sms.getDefaultSmsPackage(context))) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.d(TAG, "notifyIfNotDefaultSmsApp - called from default sms app");
+            }
+            return;
+        }
+        // Direct the intent to only the default SMS app, and only if the SMS app has a receiver
+        // for the intent.
+        ComponentName componentName =
+                SmsApplication.getDefaultExternalTelephonyProviderChangedApplication(context, true);
+        if (componentName == null) {
+            return;     // the default sms app doesn't have a receiver for this intent
+        }
+
+        final Intent intent =
+                new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE);
+        intent.setComponent(componentName);
+        if (uri != null) {
+            intent.setData(uri);
+        }
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.d(TAG, "notifyIfNotDefaultSmsApp - called from " + callingPackage + ", notifying");
+        }
+        context.sendBroadcast(intent);
+    }
+
 }
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index d48f1c6..c282a7f 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -17,9 +17,12 @@
 package com.android.providers.telephony;
 
 import android.app.AppOpsManager;
+import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
@@ -41,6 +44,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.telephony.SmsApplication;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -402,6 +407,7 @@
 
         int match = sURLMatcher.match(url);
         String table = TABLE_SMS;
+        boolean notifyIfNotDefault = true;
 
         switch (match) {
             case SMS_ALL:
@@ -440,6 +446,10 @@
 
             case SMS_RAW_MESSAGE:
                 table = "raw";
+                // The raw table is used by the telephony layer for storing an sms before
+                // sending out a notification that an sms has arrived. We don't want to notify
+                // the default sms app of changes to this table.
+                notifyIfNotDefault = false;
                 break;
 
             case SMS_STATUS_PENDING:
@@ -575,7 +585,7 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.d(TAG, "insert " + uri + " succeeded");
             }
-            notifyChange(uri);
+            notifyChange(notifyIfNotDefault, uri, callerPkg);
             return uri;
         } else {
             Log.e(TAG,"insert: failed!");
@@ -589,6 +599,7 @@
         int count;
         int match = sURLMatcher.match(url);
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        boolean notifyIfNotDefault = true;
         switch (match) {
             case SMS_ALL:
                 count = db.delete(TABLE_SMS, where, whereArgs);
@@ -627,6 +638,7 @@
 
             case SMS_RAW_MESSAGE:
                 count = db.delete("raw", where, whereArgs);
+                notifyIfNotDefault = false;
                 break;
 
             case SMS_STATUS_PENDING:
@@ -643,7 +655,7 @@
         }
 
         if (count > 0) {
-            notifyChange(url);
+            notifyChange(notifyIfNotDefault, url, getCallingPackage());
         }
         return count;
     }
@@ -678,11 +690,13 @@
         int count = 0;
         String table = TABLE_SMS;
         String extraWhere = null;
+        boolean notifyIfNotDefault = true;
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
 
         switch (sURLMatcher.match(url)) {
             case SMS_RAW_MESSAGE:
                 table = TABLE_RAW;
+                notifyIfNotDefault = false;
                 break;
 
             case SMS_STATUS_PENDING:
@@ -747,17 +761,21 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.d(TAG, "update " + url + " succeeded");
             }
-            notifyChange(url);
+            notifyChange(notifyIfNotDefault, url, callerPkg);
         }
         return count;
     }
 
-    private void notifyChange(Uri uri) {
-        ContentResolver cr = getContext().getContentResolver();
+    private void notifyChange(boolean notifyIfNotDefault, Uri uri, final String callingPackage) {
+        final Context context = getContext();
+        ContentResolver cr = context.getContentResolver();
         cr.notifyChange(uri, null, true, UserHandle.USER_ALL);
         cr.notifyChange(MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
         cr.notifyChange(Uri.parse("content://mms-sms/conversations/"), null, true,
                 UserHandle.USER_ALL);
+        if (notifyIfNotDefault) {
+            ProviderUtil.notifyIfNotDefaultSmsApp(uri, callingPackage, context);
+        }
     }
 
     private SQLiteOpenHelper mOpenHelper;