From 43374eabb8669b79c734da49177b9ed1a7651237 Mon Sep 17 00:00:00 2001 From: Hongming Jin Date: Fri, 14 Dec 2018 15:32:39 -0800 Subject: [PATCH] Add special apps access settings page for financial app. Test: manual test in settings app Bug:111207447 Change-Id: Ifa3afba2fbfd2f874deeea35f5735ac3459eed17 --- res/values/strings.xml | 6 + res/xml/financial_app_sms_access.xml | 22 +++ res/xml/special_access.xml | 5 + .../FinancialAppsController.java | 158 ++++++++++++++++++ .../financialapps/FinancialAppsSmsAccess.java | 64 +++++++ .../FinancialAppsControllerTest.java | 125 ++++++++++++++ 6 files changed, 380 insertions(+) create mode 100644 res/xml/financial_app_sms_access.xml create mode 100644 src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsController.java create mode 100644 src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsSmsAccess.java create mode 100644 tests/robotests/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 6aacb0a8da6..a1fb013c8e1 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6930,6 +6930,8 @@ network, mobile network state, service state, signal strength, mobile network type, roaming, iccid serial number, hardware version android security patch level, baseband version, kernel version + + financial app, sms, permission dark theme @@ -8820,6 +8822,10 @@ %1$d of %2$d apps allowed to modify system settings + + Financial Apps Sms Access + + financial_sms_root_screen_key Can install other apps diff --git a/res/xml/financial_app_sms_access.xml b/res/xml/financial_app_sms_access.xml new file mode 100644 index 00000000000..6f7ba50dd4e --- /dev/null +++ b/res/xml/financial_app_sms_access.xml @@ -0,0 +1,22 @@ + + + diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml index 57e673fdba3..cb766935aa1 100644 --- a/res/xml/special_access.xml +++ b/res/xml/special_access.xml @@ -130,4 +130,9 @@ android:value="com.android.settings.Settings$ChangeWifiStateActivity" /> + diff --git a/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsController.java b/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsController.java new file mode 100644 index 00000000000..492e4fd2566 --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsController.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018 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.settings.applications.specialaccess.financialapps; + +import static android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS; +import static android.Manifest.permission.READ_SMS; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.Preference.OnPreferenceChangeListener; +import androidx.preference.SwitchPreference; + +import java.util.ArrayList; +import java.util.List; + +public class FinancialAppsController extends BasePreferenceController + implements ApplicationsState.Callbacks { + private final static String TAG = FinancialAppsController.class.getSimpleName(); + + @VisibleForTesting + PreferenceScreen mRoot; + + public FinancialAppsController(Context context, String key) { + super(context, key); + } + + @AvailabilityStatus + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mRoot = screen; + } + + @Override + public void updateState(Preference preference) { + updateList(); + } + + private void updateList() { + mRoot.removeAll(); + + final PackageManager packageManager = mContext.getPackageManager(); + final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); + + final List installedPackages = + packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS); + final int numPackages = installedPackages.size(); + for (int i = 0; i < numPackages; i++) { + final PackageInfo installedPackage = installedPackages.get(i); + + if (installedPackage.requestedPermissions == null) { + continue; + } + final int targetSdk = installedPackage.applicationInfo.targetSdkVersion; + final String pkgName = installedPackage.packageName; + + if ((targetSdk >= Build.VERSION_CODES.Q + && ArrayUtils.contains(installedPackage.requestedPermissions, + SMS_FINANCIAL_TRANSACTIONS)) + || (targetSdk < Build.VERSION_CODES.Q + && ArrayUtils.contains(installedPackage.requestedPermissions, + READ_SMS))) { + final SwitchPreference pref = new SwitchPreference(mRoot.getContext()); + pref.setTitle(installedPackage.applicationInfo.loadLabel(packageManager)); + pref.setKey(pkgName); + + pref.setChecked( + appOpsManager.checkOp( + targetSdk >= Build.VERSION_CODES.Q + ? AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS + : AppOpsManager.OP_READ_SMS, + installedPackage.applicationInfo.uid, + pkgName) == AppOpsManager.MODE_ALLOWED); + + pref.setOnPreferenceChangeListener((preference, newValue) -> { + final int uid; + try { + uid = packageManager.getPackageInfo(preference.getKey(), 0) + .applicationInfo.uid; + } catch (NameNotFoundException e) { + Log.e(TAG, "onPreferenceChange: Failed to get uid for " + + preference.getKey()); + return false; + } + + appOpsManager.setMode( + targetSdk >= Build.VERSION_CODES.Q + ? AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS + : AppOpsManager.OP_READ_SMS, + uid, + pkgName, + (Boolean) newValue ? AppOpsManager.MODE_ALLOWED + : AppOpsManager.MODE_IGNORED); + return true; + }); + mRoot.addPreference(pref); + } + } + } + + @Override + public void onRunningStateChanged(boolean running) {} + + @Override + public void onPackageListChanged() { + updateList(); + } + + @Override + public void onRebuildComplete(ArrayList apps) {} + + @Override + public void onPackageIconChanged() {} + + @Override + public void onPackageSizeChanged(String packageName) {} + + @Override + public void onAllSizesComputed() {} + + @Override + public void onLauncherInfoChanged() {} + + @Override + public void onLoadEntriesCompleted() {} +} diff --git a/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsSmsAccess.java b/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsSmsAccess.java new file mode 100644 index 00000000000..92f4e287bfd --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsSmsAccess.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 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.settings.applications.specialaccess.financialapps; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.provider.SearchIndexableResource; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.List; + +@SearchIndexable +public class FinancialAppsSmsAccess extends DashboardFragment { + private final static String TAG = FinancialAppsSmsAccess.class.getSimpleName(); + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.financial_app_sms_access; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.SETTINGS_FINANCIAL_APPS_SMS_ACCESS; + } + + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex(Context context, + boolean enabled) { + final ArrayList result = new ArrayList<>(); + + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.financial_app_sms_access; + result.add(sir); + return result; + } + }; +} diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsControllerTest.java new file mode 100644 index 00000000000..39a05cb012a --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/financialapps/FinancialAppsControllerTest.java @@ -0,0 +1,125 @@ +package com.android.settings.applications.specialaccess.financialapps; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import static android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS; +import static android.Manifest.permission.READ_SMS; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; + +import java.util.ArrayList; +import java.util.Arrays; + +@RunWith(RobolectricTestRunner.class) +public class FinancialAppsControllerTest { + @Mock + private PackageManager mPackageManager; + @Mock + private AppOpsManager mAppOpsManager; + @Mock + private PreferenceScreen mRoot; + @Mock + private Preference mPreference; + + private Context mContext; + private PackageInfo mPackageInfoNoPermissionRequested; + private PackageInfo mPackageInfoPermissionRequestedQPlus; + private PackageInfo mPackageInfoPermissionRequestedPreQ; + private FinancialAppsController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager); + + initializePackageInfos(); + + mController = new FinancialAppsController(mContext, "key"); + mController.displayPreference(mRoot); + } + + private void initializePackageInfos() { + mPackageInfoNoPermissionRequested = new PackageInfo(); + mPackageInfoNoPermissionRequested.applicationInfo = new ApplicationInfo(); + + mPackageInfoPermissionRequestedQPlus = new PackageInfo(); + mPackageInfoPermissionRequestedQPlus.applicationInfo = new ApplicationInfo(); + // TODO(b/121161546): update after robolectric test support Q + //mPackageInfoPermissionRequestedQPlus.applicationInfo.targetSdkVersion = + // Build.VERSION_CODES.Q; + mPackageInfoPermissionRequestedQPlus.applicationInfo.uid = 2001; + mPackageInfoPermissionRequestedQPlus.applicationInfo.nonLocalizedLabel = "QPLUS Package"; + mPackageInfoPermissionRequestedQPlus.packageName = "QPLUS"; + mPackageInfoPermissionRequestedQPlus.requestedPermissions = + new String[] {SMS_FINANCIAL_TRANSACTIONS}; + + mPackageInfoPermissionRequestedPreQ = new PackageInfo(); + mPackageInfoPermissionRequestedPreQ.applicationInfo = new ApplicationInfo(); + mPackageInfoPermissionRequestedPreQ.applicationInfo.targetSdkVersion = Build.VERSION_CODES.M; + mPackageInfoPermissionRequestedPreQ.applicationInfo.uid = 2002; + mPackageInfoPermissionRequestedPreQ.applicationInfo.nonLocalizedLabel = "PREQ Package"; + mPackageInfoPermissionRequestedPreQ.packageName = "PREQ"; + mPackageInfoPermissionRequestedPreQ.requestedPermissions = new String[] {READ_SMS}; + } + + @Test + public void isAvailable_true() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void noPreferenceAddedWhenNoPackageRequestPermission() { + when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS)) + .thenReturn(new ArrayList( + Arrays.asList(mPackageInfoNoPermissionRequested))); + mController.updateState(null); + assertThat(mController.mRoot.getPreferenceCount()).isEqualTo(0); + } + + //TODO(b/121161546): Add these tests after robolectric test support Q + /* + @Test + public void preferenceAddedWhenPreQPackageRequestPermission() { + when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS)) + .thenReturn(new ArrayList( + Arrays.asList(mPackageInfoPermissionRequestedPreQ))); + mController.updateState(null); + assertThat(mController.mRoot.getPreferenceCount()).isEqualTo(1); + SwitchPreference pref = (SwitchPreference) mController.mRoot.getPreference(0); + assertThat(pref).isNotNull(); + } + + @Test + public void preferenceAddedWhenQPlusPackageRequestPermission() { + when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS)) + .thenReturn(new ArrayList( + Arrays.asList(mPackageInfoPermissionRequestedQPlus))); + mController.updateState(null); + assertThat(mController.mRoot.getPreferenceCount()).isEqualTo(1); + SwitchPreference pref = (SwitchPreference) mController.mRoot.getPreference(0); + assertThat(pref).isNotNull(); + }*/ +}