Add special apps access settings page for financial app.

Test: manual test in settings app
Bug:111207447
Change-Id: Ifa3afba2fbfd2f874deeea35f5735ac3459eed17
This commit is contained in:
Hongming Jin
2018-12-14 15:32:39 -08:00
parent 8c1cfbdf91
commit 43374eabb8
6 changed files with 380 additions and 0 deletions

View File

@@ -6930,6 +6930,8 @@
<string name="keywords_sim_status">network, mobile network state, service state, signal strength, mobile network type, roaming, iccid</string> <string name="keywords_sim_status">network, mobile network state, service state, signal strength, mobile network type, roaming, iccid</string>
<string name="keywords_model_and_hardware">serial number, hardware version</string> <string name="keywords_model_and_hardware">serial number, hardware version</string>
<string name="keywords_android_version">android security patch level, baseband version, kernel version</string> <string name="keywords_android_version">android security patch level, baseband version, kernel version</string>
<!--Search keywords for financial apps sms access settings -->
<string name="keywords_financial_apps_sms_access">financial app, sms, permission</string>
<!-- Search keyword for Device Theme Settings [CHAR LIMIT=NONE] --> <!-- Search keyword for Device Theme Settings [CHAR LIMIT=NONE] -->
<string name="keywords_systemui_theme">dark theme</string> <string name="keywords_systemui_theme">dark theme</string>
@@ -8820,6 +8822,10 @@
<!-- Summary of number of apps currently can write system settings [CHAR LIMIT=60] --> <!-- Summary of number of apps currently can write system settings [CHAR LIMIT=60] -->
<string name="write_settings_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed to modify system settings</string> <string name="write_settings_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed to modify system settings</string>
<!-- Settings title in main settings screen for financial apps sms access [CHAR LIMIT=60] -->
<string name="financial_apps_sms_access_title">Financial Apps Sms Access</string>
<!-- Preference key for financial apps sms access screen -->
<string name="financial_sms_root_screen_key" translatable="false">financial_sms_root_screen_key</string>
<!-- Label for showing apps that can install other apps [CHAR LIMIT=45] --> <!-- Label for showing apps that can install other apps [CHAR LIMIT=45] -->
<string name="filter_install_sources_apps">Can install other apps</string> <string name="filter_install_sources_apps">Can install other apps</string>
<!-- Label for showing apps that can write system settings [CHAR LIMIT=45] --> <!-- Label for showing apps that can write system settings [CHAR LIMIT=45] -->

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="@string/financial_sms_root_screen_key"
android:title="@string/financial_apps_sms_access_title"
settings:controller="com.android.settings.applications.specialaccess.financialapps.FinancialAppsController" />

View File

@@ -130,4 +130,9 @@
android:value="com.android.settings.Settings$ChangeWifiStateActivity" /> android:value="com.android.settings.Settings$ChangeWifiStateActivity" />
</Preference> </Preference>
<Preference
android:key="financial_apps_sms_access"
android:title="@string/financial_apps_sms_access_title"
android:fragment="com.android.settings.applications.specialaccess.financialapps.FinancialAppsSmsAccess"
settings:keywords="@string/keywords_financial_apps_sms_access" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -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<PackageInfo> 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<AppEntry> apps) {}
@Override
public void onPackageIconChanged() {}
@Override
public void onPackageSizeChanged(String packageName) {}
@Override
public void onAllSizesComputed() {}
@Override
public void onLauncherInfoChanged() {}
@Override
public void onLoadEntriesCompleted() {}
}

View File

@@ -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<SearchIndexableResource> getXmlResourcesToIndex(Context context,
boolean enabled) {
final ArrayList<SearchIndexableResource> result = new ArrayList<>();
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.financial_app_sms_access;
result.add(sir);
return result;
}
};
}

View File

@@ -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<PackageInfo>(
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<PackageInfo>(
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<PackageInfo>(
Arrays.asList(mPackageInfoPermissionRequestedQPlus)));
mController.updateState(null);
assertThat(mController.mRoot.getPreferenceCount()).isEqualTo(1);
SwitchPreference pref = (SwitchPreference) mController.mRoot.getPreference(0);
assertThat(pref).isNotNull();
}*/
}