From 02e65a181ae3b8b757b520f599b2678d06ce0c2f Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Wed, 13 May 2015 10:39:35 -0700 Subject: [PATCH] Access mock location is no longer a runtime permission - settings The access mock location is no longer a runtime permission. It is a signature protected one that apps cannot get but the fact they request it means they want to inject location into the system. Now the user gets to choose the current mock location app in developer options from the apps that request the mock location permission. The access to mock location is no longer guarded by the permisson but from a new app op which is off by default and the settiings UI sets it to enabled only for the currently selected mock location app. bug:21078873 Change-Id: I6555179ecf0cc37d9bb857e9dfb3b04c091ea612 --- res/values/strings.xml | 12 +- res/xml/development_prefs.xml | 6 +- src/com/android/settings/AppPicker.java | 52 ++++++++- .../android/settings/DevelopmentSettings.java | 110 ++++++++++++++++-- 4 files changed, 156 insertions(+), 24 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index ebf1fb451fd..4291581ab0e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3540,10 +3540,14 @@ Logger buffer sizes Select Logger sizes per log buffer - - Allow mock locations - - Allow mock locations + + + Select mock location app + + No mock location app set + + Mock location app: %1$s + Enable view attribute inspection diff --git a/res/xml/development_prefs.xml b/res/xml/development_prefs.xml index 39866c9e519..32c96c16b4e 100644 --- a/res/xml/development_prefs.xml +++ b/res/xml/development_prefs.xml @@ -86,10 +86,8 @@ android:title="@string/bugreport_in_power" android:summary="@string/bugreport_in_power_summary"/> - + packageOps = appOpsManager.getPackagesForOps(MOCK_LOCATOIN_APP_OPS); + if (packageOps != null) { + // Should be one but in case we are in a bad state due to use of command line tools. + for (PackageOps packageOp : packageOps) { + if (packageOp.getOps().get(0).getMode() != AppOpsManager.MODE_ERRORED) { + String oldMockLocationApp = packageOp.getPackageName(); + try { + ApplicationInfo ai = getActivity().getPackageManager().getApplicationInfo( + oldMockLocationApp, PackageManager.GET_DISABLED_COMPONENTS); + appOpsManager.setMode(AppOpsManager.OP_MOCK_LOCATION, ai.uid, + oldMockLocationApp, AppOpsManager.MODE_ERRORED); + } catch (NameNotFoundException e) { + /* ignore */ + } + } + } + } + + // Enable the app op of the new mock location app if such. + if (!TextUtils.isEmpty(mMockLocationApp)) { + try { + ApplicationInfo ai = getActivity().getPackageManager().getApplicationInfo( + mMockLocationApp, PackageManager.GET_DISABLED_COMPONENTS); + appOpsManager.setMode(AppOpsManager.OP_MOCK_LOCATION, ai.uid, + mMockLocationApp, AppOpsManager.MODE_ALLOWED); + } catch (NameNotFoundException e) { + /* ignore */ + } + } + } + private static void resetDebuggerOptions() { try { ActivityManagerNative.getDefault().setDebugApp( @@ -709,6 +757,39 @@ public class DevelopmentSettings extends SettingsPreferenceFragment } } + private void updateMockLocation() { + AppOpsManager appOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); + + List packageOps = appOpsManager.getPackagesForOps(MOCK_LOCATOIN_APP_OPS); + if (packageOps != null) { + for (PackageOps packageOp : packageOps) { + if (packageOp.getOps().get(0).getMode() == AppOpsManager.MODE_ALLOWED) { + mMockLocationApp = packageOps.get(0).getPackageName(); + break; + } + } + } + + if (!TextUtils.isEmpty(mMockLocationApp)) { + String label = mMockLocationApp; + try { + ApplicationInfo ai = getActivity().getPackageManager().getApplicationInfo( + mMockLocationApp, PackageManager.GET_DISABLED_COMPONENTS); + CharSequence appLabel = getPackageManager().getApplicationLabel(ai); + if (appLabel != null) { + label = appLabel.toString(); + } + } catch (PackageManager.NameNotFoundException e) { + /* ignore */ + } + + mMockLocationAppPref.setSummary(getString(R.string.mock_location_app_set, label)); + mHaveDebugSettings = true; + } else { + mMockLocationAppPref.setSummary(getString(R.string.mock_location_app_not_set)); + } + } + private void updateVerifyAppsOverUsbOptions() { updateSwitchPreference(mVerifyAppsOverUsb, Settings.Global.getInt(getActivity().getContentResolver(), Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0); @@ -1506,6 +1587,12 @@ public class DevelopmentSettings extends SettingsPreferenceFragment writeDebuggerOptions(); updateDebuggerOptions(); } + } else if (requestCode == RESULT_MOCK_LOCATION_APP) { + if (resultCode == Activity.RESULT_OK) { + mMockLocationApp = data.getAction(); + writeMockLocation(); + updateMockLocation(); + } } else if (requestCode == REQUEST_CODE_ENABLE_OEM_UNLOCK) { if (resultCode == Activity.RESULT_OK) { if (mEnableOemUnlock.isChecked()) { @@ -1574,16 +1661,19 @@ public class DevelopmentSettings extends SettingsPreferenceFragment Utils.setOemUnlockEnabled(getActivity(), false); } } - } else if (preference == mAllowMockLocation) { - Settings.Secure.putInt(getActivity().getContentResolver(), - Settings.Secure.ALLOW_MOCK_LOCATION, - mAllowMockLocation.isChecked() ? 1 : 0); + } else if (preference == mMockLocationAppPref) { + Intent intent = new Intent(getActivity(), AppPicker.class); + intent.putExtra(AppPicker.EXTRA_REQUESTIING_PERMISSION, + Manifest.permission.ACCESS_MOCK_LOCATION); + startActivityForResult(intent, RESULT_MOCK_LOCATION_APP); } else if (preference == mDebugViewAttributes) { Settings.Global.putInt(getActivity().getContentResolver(), Settings.Global.DEBUG_VIEW_ATTRIBUTES, mDebugViewAttributes.isChecked() ? 1 : 0); } else if (preference == mDebugAppPref) { - startActivityForResult(new Intent(getActivity(), AppPicker.class), RESULT_DEBUG_APP); + Intent intent = new Intent(getActivity(), AppPicker.class); + intent.putExtra(AppPicker.EXTRA_DEBUGGABLE, true); + startActivityForResult(intent, RESULT_DEBUG_APP); } else if (preference == mWaitForDebugger) { writeDebuggerOptions(); } else if (preference == mVerifyAppsOverUsb) {