summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorke Lee <yorkelee@google.com>2015-05-02 22:22:54 -0700
committerYorke Lee <yorkelee@google.com>2015-05-11 11:39:05 -0700
commit4aece9536ed64da965fd42da07a393f935abd6b1 (patch)
treef7b55745b75b1f2bcaa40fddd4c1701e72b92d18
parent81c93829582aac6cd8717511216611f37cc9c4c5 (diff)
Dynamically check for permission denials
Don't crash when performing certain operations that the dialer might not have permissions to execute: * Access voicemail provider * Certain TelecomManager methods Add a class TelecomUtil that checks for permissions/default dialer status, and handles the privileged operations appropriately. Bug: 20266292 Change-Id: Ibe8a85440b9ca20169b5ce3be24d1a385caaebb6
-rw-r--r--src/com/android/dialer/CallDetailActivity.java7
-rw-r--r--src/com/android/dialer/SpecialCharSequenceMgr.java8
-rw-r--r--src/com/android/dialer/calllog/CallLogActivity.java2
-rw-r--r--src/com/android/dialer/calllog/CallLogFragment.java20
-rw-r--r--src/com/android/dialer/calllog/CallLogNotificationsHelper.java12
-rw-r--r--src/com/android/dialer/calllog/CallLogNotificationsService.java23
-rw-r--r--src/com/android/dialer/calllog/CallLogQueryHandler.java23
-rw-r--r--src/com/android/dialer/calllog/CallLogReceiver.java10
-rw-r--r--src/com/android/dialer/calllog/ContactInfoHelper.java5
-rw-r--r--src/com/android/dialer/calllog/IntentProvider.java5
-rw-r--r--src/com/android/dialer/util/TelecomUtil.java120
11 files changed, 192 insertions, 43 deletions
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index 7a23944a0..0a295c4fe 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -66,6 +66,7 @@ import com.android.dialer.util.AsyncTaskExecutor;
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 @@ public class CallDetailActivity extends Activity implements ProximitySensorAware
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 @@ public class CallDetailActivity extends Activity implements ProximitySensorAware
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 36145aaef..a57e2ee57 100644
--- a/src/com/android/dialer/SpecialCharSequenceMgr.java
+++ b/src/com/android/dialer/SpecialCharSequenceMgr.java
@@ -46,6 +46,7 @@ import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
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 @@ public class SpecialCharSequenceMgr {
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 @@ public class SpecialCharSequenceMgr {
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 c4e16a1b7..8a0cc1364 100644
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ b/src/com/android/dialer/calllog/CallLogActivity.java
@@ -193,7 +193,7 @@ public class CallLogActivity extends Activity implements CallLogQueryHandler.Lis
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 85fca9139..02970f66c 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -23,6 +23,7 @@ import android.app.Activity;
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 @@ public class CallLogFragment extends Fragment
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 f6ee8966b..367cb78c3 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 @@ import android.telecom.TelecomManager;
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 2e0e50258..22809db8c 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 @@ public class CallLogNotificationsService extends IntentService {
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 7eb5f8a0b..49d6a4121 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -19,6 +19,7 @@ package com.android.dialer.calllog;
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.provider.VoicemailContract.Voicemails;
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 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
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 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
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 class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
}
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 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
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 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
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 97d2951c1..fef76086c 100644
--- a/src/com/android/dialer/calllog/CallLogReceiver.java
+++ b/src/com/android/dialer/calllog/CallLogReceiver.java
@@ -34,15 +34,9 @@ public class CallLogReceiver extends BroadcastReceiver {
@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 8e8aa3ce1..38c9bba87 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.PhoneNumberHelper;
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 @@ public class ContactInfoHelper {
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 4be2d1c9e..9f5150ac7 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.CallDetailActivity;
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 @@ public abstract class IntentProvider {
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 000000000..1cd270c9b
--- /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);
+ }
+}