Merge "Notify the system dialer when the Carrier VVM app is installed" into oc-dev am: b8aa08a1c1
am: ca50dd409c

Change-Id: I67a635c18fc49ba8c1fdaab8bbe57c80eed24e0c
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 57cae70..5ef97c0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -76,6 +76,7 @@
     <protected-broadcast android:name= "com.android.ims.REGISTRATION_ERROR" />
     <protected-broadcast android:name= "com.android.phone.vvm.omtp.sms.REQUEST_SENT" />
     <protected-broadcast android:name= "com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT" />
+    <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED" />
 
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 39937eb..20ecbf5 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -57,6 +57,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.phone.common.CallLogAsync;
 import com.android.phone.settings.SettingsConstants;
+import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
 import com.android.services.telephony.sip.SipUtil;
 
 /**
@@ -157,6 +158,9 @@
     // Broadcast receiver for various intent broadcasts (see onCreate())
     private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
 
+    private final CarrierVvmPackageInstalledReceiver mCarrierVvmPackageInstalledReceiver =
+            new CarrierVvmPackageInstalledReceiver();
+
     Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -333,6 +337,8 @@
             intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
             registerReceiver(mReceiver, intentFilter);
 
+            mCarrierVvmPackageInstalledReceiver.register(this);
+
             //set the default values for the preferences in the phone.
             PreferenceManager.setDefaultValues(this, R.xml.network_setting_fragment, false);
 
diff --git a/src/com/android/phone/vvm/CarrierVvmPackageInstalledReceiver.java b/src/com/android/phone/vvm/CarrierVvmPackageInstalledReceiver.java
new file mode 100644
index 0000000..ec0d3f6
--- /dev/null
+++ b/src/com/android/phone/vvm/CarrierVvmPackageInstalledReceiver.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 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.phone.vvm;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PersistableBundle;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.telephony.VisualVoicemailService;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Receives {@link Intent#ACTION_PACKAGE_ADDED} for the system dialer to inform it a carrier visual
+ * voicemail app has been installed. ACTION_PACKAGE_ADDED requires the receiver process to be
+ * running so the system dialer cannot receive it itself.
+ *
+ * Carrier VVM apps are usually regular apps, not a
+ * {@link VisualVoicemailService} nor {@link android.service.carrier.CarrierMessagingService} so it
+ * will not take precedence over the system dialer. The system dialer should disable VVM it self
+ * to let the carrier app take over since the installation is an explicit user interaction. Carrier
+ * customer support might also ask the user to switch to their app if they believe there's any
+ * issue in the system dialer so this transition should not require more user interaction.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY
+ */
+public class CarrierVvmPackageInstalledReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "VvmPkgInstalledRcvr";
+
+    /**
+     * Hidden broadcast to the system dialer
+     */
+    private static final String ACTION_CARRIER_VVM_PACKAGE_INSTALLED =
+            "com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED";
+
+    public void register(Context context) {
+        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addDataScheme("package");
+        context.registerReceiver(this, intentFilter);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent.getData() == null) {
+            return;
+        }
+        String packageName = intent.getData().getSchemeSpecificPart();
+        if (packageName == null) {
+            return;
+        }
+
+        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+        String systemDialer = telecomManager.getSystemDialerPackage();
+        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+        for (PhoneAccountHandle phoneAccountHandle : telecomManager.getCallCapablePhoneAccounts()) {
+            TelephonyManager pinnedTelephonyManager = telephonyManager
+                    .createForPhoneAccountHandle(phoneAccountHandle);
+
+            if (pinnedTelephonyManager == null) {
+                VvmLog.e(TAG, "cannot create TelephonyManager from " + phoneAccountHandle);
+                continue;
+            }
+
+            if (!getCarrierVvmPackages(telephonyManager).contains(packageName)) {
+                continue;
+            }
+
+            VvmLog.i(TAG, "Carrier VVM app " + packageName + " installed");
+
+            String vvmPackage = pinnedTelephonyManager.getVisualVoicemailPackageName();
+            if (!TextUtils.equals(vvmPackage, systemDialer)) {
+                // Non system dialer do not need to prioritize carrier vvm app.
+                VvmLog.i(TAG, "non system dialer " + vvmPackage + " ignored");
+                continue;
+            }
+
+            VvmLog.i(TAG, "sending broadcast to " + vvmPackage);
+            Intent broadcast = new Intent(ACTION_CARRIER_VVM_PACKAGE_INSTALLED);
+            broadcast.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+            broadcast.setPackage(vvmPackage);
+            context.sendBroadcast(broadcast);
+        }
+    }
+
+    private static Set<String> getCarrierVvmPackages(TelephonyManager pinnedTelephonyManager) {
+        Set<String> carrierPackages = new ArraySet<>();
+
+        PersistableBundle config = pinnedTelephonyManager.getCarrierConfig();
+        String singlePackage = config
+                .getString(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING);
+        if (!TextUtils.isEmpty(singlePackage)) {
+            carrierPackages.add(singlePackage);
+        }
+        String[] arrayPackages = config
+                .getStringArray(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY);
+        if (arrayPackages != null) {
+            Collections.addAll(carrierPackages, arrayPackages);
+        }
+
+        return carrierPackages;
+    }
+}