am 419f2020: am 4aece953: Dynamically check for permission denials

* commit '419f20207dbe0f8053fc9cd3a17e546e7bbee934':
  Dynamically check for permission denials
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index 7a23944..0a295c4 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -66,6 +66,7 @@
 import com.android.dialer.util.AsyncTaskExecutors;
 import com.android.dialer.util.CallIntentUtil;
 import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.TelecomUtil;
 import com.android.dialer.voicemail.VoicemailPlaybackFragment;
 import com.android.dialer.voicemail.VoicemailStatusHelper;
 import com.android.dialer.voicemail.VoicemailStatusHelper.StatusMessage;
@@ -307,7 +308,8 @@
         final int numIds = ids == null ? 0 : ids.length;
         final Uri[] uris = new Uri[numIds];
         for (int index = 0; index < numIds; ++index) {
-            uris[index] = ContentUris.withAppendedId(Calls.CONTENT_URI_WITH_VOICEMAIL, ids[index]);
+            uris[index] = ContentUris.withAppendedId(
+                    TelecomUtil.getCallLogUri(CallDetailActivity.this), ids[index]);
         }
         return uris;
     }
@@ -670,7 +672,8 @@
                 new AsyncTask<Void, Void, Void>() {
                     @Override
                     public Void doInBackground(Void... params) {
-                        getContentResolver().delete(Calls.CONTENT_URI_WITH_VOICEMAIL,
+                        getContentResolver().delete(
+                                TelecomUtil.getCallLogUri(CallDetailActivity.this),
                                 Calls._ID + " IN (" + callIds + ")", null);
                         return null;
                     }
diff --git a/src/com/android/dialer/SpecialCharSequenceMgr.java b/src/com/android/dialer/SpecialCharSequenceMgr.java
index 36145aa..a57e2ee 100644
--- a/src/com/android/dialer/SpecialCharSequenceMgr.java
+++ b/src/com/android/dialer/SpecialCharSequenceMgr.java
@@ -46,6 +46,7 @@
 import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
 import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
 import com.android.dialer.calllog.PhoneAccountUtils;
+import com.android.dialer.util.TelecomUtil;
 
 import java.util.Arrays;
 import java.util.ArrayList;
@@ -272,7 +273,7 @@
         sPreviousAdnQueryHandler = handler;
     }
 
-    static boolean handlePinEntry(Context context, final String input) {
+    static boolean handlePinEntry(final Context context, final String input) {
         if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) {
             final TelecomManager telecomManager =
                     (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
@@ -284,13 +285,14 @@
             if (subscriptionAccountHandles.size() == 1 || hasUserSelectedDefault) {
                 // Don't bring up the dialog for single-SIM or if the default outgoing account is
                 // a subscription account.
-                return telecomManager.handleMmi(input);
+                return TelecomUtil.handleMmi(context, input, null);
             } else if (subscriptionAccountHandles.size() > 1){
                 SelectPhoneAccountListener listener = new SelectPhoneAccountListener() {
                     @Override
                     public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle,
                             boolean setDefault) {
-                        telecomManager.handleMmi(input, selectedAccountHandle);
+                        TelecomUtil.handleMmi(context.getApplicationContext(),
+                                input, selectedAccountHandle);
                         //TODO: show error dialog if result isn't valid
                     }
                     @Override
diff --git a/src/com/android/dialer/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java
index c4e16a1..8a0cc13 100644
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ b/src/com/android/dialer/calllog/CallLogActivity.java
@@ -193,7 +193,7 @@
         mIsResumed = true;
         super.onResume();
         CallLogQueryHandler callLogQueryHandler =
-                new CallLogQueryHandler(this.getContentResolver(), this);
+                new CallLogQueryHandler(this, this.getContentResolver(), this);
         callLogQueryHandler.fetchVoicemailStatus();
         sendScreenViewForChildFragment(mViewPager.getCurrentItem());
     }
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 85fca91..02970f6 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -23,6 +23,7 @@
 import android.app.DialogFragment;
 import android.app.Fragment;
 import android.app.KeyguardManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.database.ContentObserver;
@@ -179,17 +180,16 @@
             mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit);
         }
 
-        String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
-        mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(),
-                this, mLogLimit);
+        final Activity activity = getActivity();
+        final ContentResolver resolver = activity.getContentResolver();
+        String currentCountryIso = GeoUtil.getCurrentCountryIso(activity);
+        mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit);
         mKeyguardManager =
-                (KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE);
-        getActivity().getContentResolver().registerContentObserver(
-                CallLog.CONTENT_URI, true, mCallLogObserver);
-        getActivity().getContentResolver().registerContentObserver(
-                ContactsContract.Contacts.CONTENT_URI, true, mContactsObserver);
-        getActivity().getContentResolver().registerContentObserver(
-                Status.CONTENT_URI, true, mVoicemailStatusObserver);
+                (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
+        resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);
+        resolver.registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true,
+                mContactsObserver);
+        resolver.registerContentObserver(Status.CONTENT_URI, true, mVoicemailStatusObserver);
         setHasOptionsMenu(true);
         fetchCalls();
     }
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
index f6ee896..367cb78 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
@@ -17,8 +17,8 @@
 package com.android.dialer.calllog;
 
 import android.content.Context;
-import android.content.Intent;
-import android.telecom.TelecomManager;
+
+import com.android.dialer.util.TelecomUtil;
 
 /**
  * Helper class operating on call log notifications.
@@ -26,15 +26,11 @@
 public class CallLogNotificationsHelper {
     /** Removes the missed call notifications. */
     public static void removeMissedCallNotifications(Context context) {
-        TelecomManager telecomManager = (TelecomManager)
-                context.getSystemService(Context.TELECOM_SERVICE);
-        telecomManager.cancelMissedCallsNotification();
+        TelecomUtil.cancelMissedCallsNotification(context);
     }
 
     /** Update the voice mail notifications. */
     public static void updateVoicemailNotifications(Context context) {
-        Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
-        serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS);
-        context.startService(serviceIntent);
+        CallLogNotificationsService.updateVoicemailNotifications(context, null);
     }
 }
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java
index 2e0e502..22809db 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsService.java
@@ -17,10 +17,13 @@
 package com.android.dialer.calllog;
 
 import android.app.IntentService;
+import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.util.Log;
 
+import com.android.dialer.util.TelecomUtil;
+
 /**
  * Provides operations for managing notifications.
  * <p>
@@ -84,4 +87,24 @@
             Log.d(TAG, "onHandleIntent: could not handle: " + intent);
         }
     }
+
+    /**
+     * Updates notifications for any new voicemails.
+     *
+     * @param context a valid context.
+     * @param voicemailUri The uri pointing to the voicemail to update the notification for. If
+     *         {@code null}, then notifications for all new voicemails will be updated.
+     */
+    public static void updateVoicemailNotifications(Context context, Uri voicemailUri) {
+        if (TelecomUtil.hasReadWriteVoicemailPermissions(context)) {
+            Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
+            serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS);
+            // If voicemailUri is null, then notifications for all voicemails will be updated.
+            if (voicemailUri != null) {
+                serviceIntent.putExtra(
+                        CallLogNotificationsService.EXTRA_NEW_VOICEMAIL_URI, voicemailUri);
+            }
+            context.startService(serviceIntent);
+        }
+    }
 }
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 7eb5f8a..49d6a41 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -19,6 +19,7 @@
 import android.content.AsyncQueryHandler;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabaseCorruptException;
 import android.database.sqlite.SQLiteDiskIOException;
@@ -34,6 +35,7 @@
 import android.util.Log;
 
 import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
+import com.android.dialer.util.TelecomUtil;
 import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
 
 import com.google.common.collect.Lists;
@@ -67,6 +69,8 @@
 
     private final WeakReference<Listener> mListener;
 
+    private final Context mContext;
+
     /**
      * Simple handler that wraps background calls to catch
      * {@link SQLiteException}, such as when the disk is full.
@@ -99,12 +103,15 @@
         return new CatchingWorkerHandler(looper);
     }
 
-    public CallLogQueryHandler(ContentResolver contentResolver, Listener listener) {
-        this(contentResolver, listener, -1);
+    public CallLogQueryHandler(Context context, ContentResolver contentResolver,
+            Listener listener) {
+        this(context, contentResolver, listener, -1);
     }
 
-    public CallLogQueryHandler(ContentResolver contentResolver, Listener listener, int limit) {
+    public CallLogQueryHandler(Context context, ContentResolver contentResolver, Listener listener,
+            int limit) {
         super(contentResolver);
+        mContext = context.getApplicationContext();
         mListener = new WeakReference<Listener>(listener);
         mLogLimit = limit;
     }
@@ -125,8 +132,10 @@
     }
 
     public void fetchVoicemailStatus() {
-        startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null, Status.CONTENT_URI,
-                VoicemailStatusHelperImpl.PROJECTION, null, null, null);
+        if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
+            startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null, Status.CONTENT_URI,
+                    VoicemailStatusHelperImpl.PROJECTION, null, null, null);
+        }
     }
 
     /** Fetches the list of calls in the call log. */
@@ -163,7 +172,7 @@
 
         final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
         final String selection = where.length() > 0 ? where.toString() : null;
-        Uri uri = Calls.CONTENT_URI_WITH_VOICEMAIL.buildUpon()
+        Uri uri = TelecomUtil.getCallLogUri(mContext).buildUpon()
                 .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
                 .build();
         startQuery(token, null, uri,
@@ -186,7 +195,7 @@
         ContentValues values = new ContentValues(1);
         values.put(Calls.NEW, "0");
 
-        startUpdate(UPDATE_MARK_AS_OLD_TOKEN, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
+        startUpdate(UPDATE_MARK_AS_OLD_TOKEN, null, TelecomUtil.getCallLogUri(mContext),
                 values, where.toString(), null);
     }
 
diff --git a/src/com/android/dialer/calllog/CallLogReceiver.java b/src/com/android/dialer/calllog/CallLogReceiver.java
index 97d2951..fef7608 100644
--- a/src/com/android/dialer/calllog/CallLogReceiver.java
+++ b/src/com/android/dialer/calllog/CallLogReceiver.java
@@ -34,15 +34,9 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         if (VoicemailContract.ACTION_NEW_VOICEMAIL.equals(intent.getAction())) {
-            Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
-            serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS);
-            serviceIntent.putExtra(
-                    CallLogNotificationsService.EXTRA_NEW_VOICEMAIL_URI, intent.getData());
-            context.startService(serviceIntent);
+            CallLogNotificationsService.updateVoicemailNotifications(context, intent.getData());
         } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
-            Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
-            serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS);
-            context.startService(serviceIntent);
+            CallLogNotificationsService.updateVoicemailNotifications(context, null);
         } else {
             Log.w(TAG, "onReceive: could not handle: " + intent);
         }
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index 8e8aa3c..38c9bba 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -34,6 +34,7 @@
 import com.android.contacts.common.util.UriUtils;
 import com.android.dialer.service.CachedNumberLookupService;
 import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo;
+import com.android.dialer.util.TelecomUtil;
 import com.android.dialerbind.ObjectFactory;
 
 import org.json.JSONException;
@@ -368,13 +369,13 @@
         try {
             if (countryIso == null) {
                 mContext.getContentResolver().update(
-                        Calls.CONTENT_URI_WITH_VOICEMAIL,
+                        TelecomUtil.getCallLogUri(mContext),
                         values,
                         Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " IS NULL",
                         new String[]{ number });
             } else {
                 mContext.getContentResolver().update(
-                        Calls.CONTENT_URI_WITH_VOICEMAIL,
+                        TelecomUtil.getCallLogUri(mContext),
                         values,
                         Calls.NUMBER + " = ? AND " + Calls.COUNTRY_ISO + " = ?",
                         new String[]{ number, countryIso });
diff --git a/src/com/android/dialer/calllog/IntentProvider.java b/src/com/android/dialer/calllog/IntentProvider.java
index 4be2d1c..9f5150a 100644
--- a/src/com/android/dialer/calllog/IntentProvider.java
+++ b/src/com/android/dialer/calllog/IntentProvider.java
@@ -31,6 +31,7 @@
 import com.android.dialer.DialtactsActivity;
 import com.android.dialer.PhoneCallDetails;
 import com.android.dialer.util.CallIntentUtil;
+import com.android.dialer.util.TelecomUtil;
 
 import java.util.ArrayList;
 
@@ -125,8 +126,8 @@
                     intent.putExtra(CallDetailActivity.EXTRA_CALL_LOG_IDS, extraIds);
                 } else {
                     // If there is a single item, use the direct URI for it.
-                    intent.setData(ContentUris.withAppendedId(
-                            Calls.CONTENT_URI_WITH_VOICEMAIL, id));
+                    intent.setData(ContentUris.withAppendedId(TelecomUtil.getCallLogUri(context),
+                            id));
                 }
                 return intent;
             }
diff --git a/src/com/android/dialer/util/TelecomUtil.java b/src/com/android/dialer/util/TelecomUtil.java
new file mode 100644
index 0000000..1cd270c
--- /dev/null
+++ b/src/com/android/dialer/util/TelecomUtil.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 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.dialer.util;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.provider.CallLog.Calls;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+public class TelecomUtil {
+    private static final String TAG = "TelecomUtil";
+    private static boolean sWarningLogged = false;
+
+    public static void silenceRinger(Context context) {
+        if (hasModifyPhoneStatePermission(context)) {
+            try {
+                getTelecomManager(context).silenceRinger();
+            } catch (SecurityException e) {
+                // Just in case
+                Log.w(TAG, "TelecomManager.silenceRinger called without permission.");
+            }
+        }
+    }
+
+    public static void cancelMissedCallsNotification(Context context) {
+        if (hasModifyPhoneStatePermission(context)) {
+            try {
+                getTelecomManager(context).cancelMissedCallsNotification();
+            } catch (SecurityException e) {
+                Log.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
+            }
+        }
+    }
+
+    public static Uri getAdnUriForPhoneAccount(Context context, PhoneAccountHandle handle) {
+        if (hasModifyPhoneStatePermission(context)) {
+            try {
+                return getTelecomManager(context).getAdnUriForPhoneAccount(handle);
+            } catch (SecurityException e) {
+                Log.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
+            }
+        }
+        return null;
+    }
+
+    public static boolean handleMmi(Context context, String dialString,
+            PhoneAccountHandle handle) {
+        if (hasModifyPhoneStatePermission(context)) {
+            try {
+                if (handle == null) {
+                    return getTelecomManager(context).handleMmi(dialString);
+                } else {
+                    return getTelecomManager(context).handleMmi(dialString, handle);
+                }
+            } catch (SecurityException e) {
+                Log.w(TAG, "TelecomManager.handleMmi called without permission.");
+            }
+        }
+        return false;
+    }
+
+    public static Uri getCallLogUri(Context context) {
+        return hasReadWriteVoicemailPermissions(context) ? Calls.CONTENT_URI_WITH_VOICEMAIL
+                : Calls.CONTENT_URI;
+    }
+
+    public static boolean hasReadWriteVoicemailPermissions(Context context) {
+        return isDefaultDialer(context)
+                || (hasPermission(context, Manifest.permission.READ_VOICEMAIL)
+                        && hasPermission(context, Manifest.permission.WRITE_VOICEMAIL));
+    }
+
+    public static boolean hasModifyPhoneStatePermission(Context context) {
+        return isDefaultDialer(context)
+                || hasPermission(context, Manifest.permission.MODIFY_PHONE_STATE);
+    }
+
+    private static boolean hasPermission(Context context, String permission) {
+        return context.checkSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    public static boolean isDefaultDialer(Context context) {
+        final boolean result = TextUtils.equals(context.getPackageName(),
+                getTelecomManager(context).getDefaultDialerPackage());
+        if (result) {
+            sWarningLogged = false;
+        } else {
+            if (!sWarningLogged) {
+                // Log only once to prevent spam.
+                Log.w(TAG, "Dialer is not currently set to be default dialer");
+                sWarningLogged = true;
+            }
+        }
+        return result;
+    }
+
+    private static TelecomManager getTelecomManager(Context context) {
+        return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+    }
+}