From 5f6c901ad062cd7d88db09b555e28ee354ca66c6 Mon Sep 17 00:00:00 2001 From: Yorke Lee Date: Fri, 24 Jul 2015 12:27:46 -0700 Subject: Protect against lack of WRITE_SYSTEM_SETTINGS AppOp Guard the following entry points with checks against the AppOp 1) Launch of SoundSettingsFragment Fallback: Send to system sound settings instead 2) onResume of SoundSettingsFragment (this can happen if the AppOp is toggled by the user in the background) Fallback: Return to main Dialer settings instead 3) Toggling of various perferences (this can happen in monkey tests) Fallback: Show toast, ignore settings chance Bug: 22660372 Change-Id: Ief5d74166d35a9718ea664f378f6930e777f2923 --- res/values/ids.xml | 1 + res/values/strings.xml | 3 ++ .../dialer/settings/DefaultRingtonePreference.java | 12 ++++++++ .../dialer/settings/DialerSettingsActivity.java | 34 ++++++++++++++++------ .../dialer/settings/SoundSettingsFragment.java | 27 +++++++++++++++++ 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/res/values/ids.xml b/res/values/ids.xml index e2f0ff593..d8f4aa877 100644 --- a/res/values/ids.xml +++ b/res/values/ids.xml @@ -17,4 +17,5 @@ + diff --git a/res/values/strings.xml b/res/values/strings.xml index eec9b0ca7..5e2476db0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -813,4 +813,7 @@ To place a call,\n turn on the Phone permission. + + + Phone app does not have permission to write to system settings. diff --git a/src/com/android/dialer/settings/DefaultRingtonePreference.java b/src/com/android/dialer/settings/DefaultRingtonePreference.java index c12e7175a..f2648cb66 100644 --- a/src/com/android/dialer/settings/DefaultRingtonePreference.java +++ b/src/com/android/dialer/settings/DefaultRingtonePreference.java @@ -16,12 +16,17 @@ package com.android.dialer.settings; +import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; import android.media.RingtoneManager; import android.net.Uri; import android.preference.RingtonePreference; import android.util.AttributeSet; +import android.widget.Toast; + +import com.android.contacts.common.util.PermissionsUtil; +import com.android.dialer.R; /** * RingtonePreference which doesn't show default ringtone setting. @@ -44,6 +49,13 @@ public class DefaultRingtonePreference extends RingtonePreference { @Override protected void onSaveRingtone(Uri ringtoneUri) { + if (!PermissionsUtil.hasAppOp(getContext(), AppOpsManager.OPSTR_WRITE_SETTINGS)) { + Toast.makeText( + getContext(), + getContext().getResources().getString(R.string.toast_cannot_write_system_settings), + Toast.LENGTH_SHORT).show(); + return; + } RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri); } diff --git a/src/com/android/dialer/settings/DialerSettingsActivity.java b/src/com/android/dialer/settings/DialerSettingsActivity.java index d54053b40..abf854c08 100644 --- a/src/com/android/dialer/settings/DialerSettingsActivity.java +++ b/src/com/android/dialer/settings/DialerSettingsActivity.java @@ -1,23 +1,22 @@ package com.android.dialer.settings; -import com.google.common.collect.Lists; - -import android.content.ComponentName; +import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import android.os.UserHandle; +import android.os.Process; import android.os.UserManager; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; -import android.preference.PreferenceActivity.Header; +import android.provider.Settings; import android.telecom.TelecomManager; import android.telephony.TelephonyManager; -import android.text.TextUtils; +import android.util.Log; import android.view.MenuItem; +import android.widget.Toast; -import com.android.dialer.DialtactsActivity; +import com.android.contacts.common.util.PermissionsUtil; import com.android.dialer.R; import java.util.List; @@ -26,8 +25,6 @@ public class DialerSettingsActivity extends PreferenceActivity { protected SharedPreferences mPreferences; - private static final int OWNER_HANDLE_ID = 0; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -44,6 +41,7 @@ public class DialerSettingsActivity extends PreferenceActivity { Header soundSettingsHeader = new Header(); soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title; soundSettingsHeader.fragment = SoundSettingsFragment.class.getName(); + soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration; target.add(soundSettingsHeader); Header quickResponseSettingsHeader = new Header(); @@ -90,6 +88,24 @@ public class DialerSettingsActivity extends PreferenceActivity { } } + @Override + public void onHeaderClick(Header header, int position) { + if (header.id == R.id.settings_header_sounds_and_vibration) { + // If we don't have the AppOp to write to system settings, go to system sound settings + // instead. Otherwise, perform the super implementation (which launches our own + // preference fragment. + if (!PermissionsUtil.hasAppOp(this, AppOpsManager.OPSTR_WRITE_SETTINGS)) { + Toast.makeText( + this, + getResources().getString(R.string.toast_cannot_write_system_settings), + Toast.LENGTH_SHORT).show(); + startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS)); + return; + } + } + super.onHeaderClick(header, position); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { diff --git a/src/com/android/dialer/settings/SoundSettingsFragment.java b/src/com/android/dialer/settings/SoundSettingsFragment.java index 7fc9394f8..1dedcaaff 100644 --- a/src/com/android/dialer/settings/SoundSettingsFragment.java +++ b/src/com/android/dialer/settings/SoundSettingsFragment.java @@ -16,7 +16,9 @@ package com.android.dialer.settings; +import android.app.AppOpsManager; import android.content.Context; +import android.content.Intent; import android.media.RingtoneManager; import android.os.Bundle; import android.os.Handler; @@ -30,7 +32,10 @@ import android.preference.PreferenceScreen; import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.telephony.TelephonyManager; +import android.view.MenuItem; +import android.widget.Toast; +import com.android.contacts.common.util.PermissionsUtil; import com.android.dialer.R; import com.android.phone.common.util.SettingsUtil; @@ -127,6 +132,13 @@ public class SoundSettingsFragment extends PreferenceFragment public void onResume() { super.onResume(); + if (!PermissionsUtil.hasAppOp(getContext(), AppOpsManager.OPSTR_WRITE_SETTINGS)) { + // If the user launches this setting fragment, then toggles the WRITE_SYSTEM_SETTINGS + // AppOp, then close the fragment since there is nothing useful to do. + getActivity().onBackPressed(); + return; + } + if (mVibrateWhenRinging != null) { mVibrateWhenRinging.setChecked(shouldVibrateWhenRinging()); } @@ -143,6 +155,14 @@ public class SoundSettingsFragment extends PreferenceFragment */ @Override public boolean onPreferenceChange(Preference preference, Object objValue) { + if (!PermissionsUtil.hasAppOp(getContext(), AppOpsManager.OPSTR_WRITE_SETTINGS)) { + // A user shouldn't be able to get here, but this protects against monkey crashes. + Toast.makeText( + getContext(), + getResources().getString(R.string.toast_cannot_write_system_settings), + Toast.LENGTH_SHORT).show(); + return true; + } if (preference == mVibrateWhenRinging) { boolean doVibrate = (Boolean) objValue; Settings.System.putInt(getActivity().getContentResolver(), @@ -161,6 +181,13 @@ public class SoundSettingsFragment extends PreferenceFragment */ @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + if (!PermissionsUtil.hasAppOp(getContext(), AppOpsManager.OPSTR_WRITE_SETTINGS)) { + Toast.makeText( + getContext(), + getResources().getString(R.string.toast_cannot_write_system_settings), + Toast.LENGTH_SHORT).show(); + return true; + } if (preference == mPlayDtmfTone) { Settings.System.putInt(getActivity().getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING, -- cgit v1.2.3