Adding Manage External Sources Settings

Added a settings fragment to manage external sources. It lists all
applications that have either requested REQUEST_INSTALL_PACKAGES or have
their app op changed from default.

Test: Will include in follow-up CL. Tracked in b/33792674

Bug: 31002700
Change-Id: Ibd2a1922be214b62aec4eefa45f7b9691256b205
This commit is contained in:
Suprabh Shukla
2016-12-16 14:44:05 -08:00
parent 27cb8b4a65
commit 57d92315a4
8 changed files with 345 additions and 0 deletions

View File

@@ -2905,6 +2905,17 @@
android:value="com.android.settings.applications.ManageApplications" /> android:value="com.android.settings.applications.ManageApplications" />
</activity> </activity>
<activity android:name="Settings$ManageExternalSourcesActivity"
android:label="@string/install_other_apps"
android:taskAffinity="">
<intent-filter android:priority="1">
<action android:name="android.settings.action.MANAGE_EXTERNAL_SOURCES" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.applications.ManageApplications" />
</activity>
<activity android:name="Settings$AppWriteSettingsActivity" <activity android:name="Settings$AppWriteSettingsActivity"
android:label="@string/write_settings_title" android:label="@string/write_settings_title"
android:taskAffinity=""> android:taskAffinity="">

View File

@@ -7224,6 +7224,12 @@
<!-- Summary of app not allowed to draw overlay [CHAR LIMIT=60] --> <!-- Summary of app not allowed to draw overlay [CHAR LIMIT=60] -->
<string name="system_alert_window_off">No</string> <string name="system_alert_window_off">No</string>
<!-- Title for settings screen for controlling apps that can install other apps on device [CHAR LIMIT=30] -->
<string name="install_other_apps">Install other apps</string>
<!-- Keywords for setting screen for controlling apps that can install other apps on device -->
<string name="keywords_install_other_apps">install apps external unknown sources</string>
<!-- Label for setting which controls whether app is trusted to install apps on the device [CHAR LIMIT=45] -->
<string name="permit_install_other_apps">Allows to install other apps</string>
<!-- Write Settings settings --> <!-- Write Settings settings -->
<!-- Settings title in main settings screen for WRITE_SETTINGS [CHAR LIMIT=30] --> <!-- Settings title in main settings screen for WRITE_SETTINGS [CHAR LIMIT=30] -->
@@ -7233,6 +7239,8 @@
<!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] --> <!-- Summary of number of apps currently can draw overlays [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>
<!-- Label for showing apps that can install other apps [CHAR LIMIT=45] -->
<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] -->
<string name="filter_write_settings_apps">Can modify system settings</string> <string name="filter_write_settings_apps">Can modify system settings</string>
<!-- Title for the apps that are allowed to write system settings [CHAR LIMIT=60] --> <!-- Title for the apps that are allowed to write system settings [CHAR LIMIT=60] -->
@@ -7249,6 +7257,12 @@
<string name="write_settings_on">Yes</string> <string name="write_settings_on">Yes</string>
<!-- Summary of app not allowed to write system settings [CHAR LIMIT=45] --> <!-- Summary of app not allowed to write system settings [CHAR LIMIT=45] -->
<string name="write_settings_off">No</string> <string name="write_settings_off">No</string>
<!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
<string name="external_source_trusted">Yes</string>
<!-- Summary of app not trusted to install apps [CHAR LIMIT=45] -->
<string name="external_source_untrusted">No</string>
<!-- Title of switch preference that controls whether an external app source is trusted or not [CHAR LIMIT=100] -->
<string name="external_source_switch_title">Trust apps from this source</string>
<!-- Title of setting that controls gesture to open camera [CHAR LIMIT=40] --> <!-- Title of setting that controls gesture to open camera [CHAR LIMIT=40] -->
<string name="camera_gesture_title">Double twist for camera</string> <string name="camera_gesture_title">Double twist for camera</string>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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">
<SwitchPreference
android:key="external_sources_settings_switch" />
<Preference
android:key="external_sources_settings_description"
android:selectable="false" />
</PreferenceScreen>

View File

@@ -92,4 +92,15 @@
android:name="classname" android:name="classname"
android:value="com.android.settings.Settings$UsageAccessSettingsActivity" /> android:value="com.android.settings.Settings$UsageAccessSettingsActivity" />
</Preference> </Preference>
<Preference
android:key="manage_external_sources"
android:title="@string/install_other_apps"
android:fragment="com.android.settings.applications.ManageApplications"
settings:keywords="@string/keywords_install_other_apps">
<extra
android:name="classname"
android:value="com.android.settings.Settings$ManageExternalSourcesActivity" />
</Preference>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -138,6 +138,9 @@ public class Settings extends SettingsActivity {
public static class AppWriteSettingsActivity extends SettingsActivity { /* empty */ } public static class AppWriteSettingsActivity extends SettingsActivity { /* empty */ }
public static class AdvancedAppsActivity extends SettingsActivity { /* empty */ } public static class AdvancedAppsActivity extends SettingsActivity { /* empty */ }
public static class ManageExternalSourcesActivity extends SettingsActivity {
/* empty */ }
public static class WifiCallingSuggestionActivity extends SettingsActivity { /* empty */ } public static class WifiCallingSuggestionActivity extends SettingsActivity { /* empty */ }
public static class ZenModeAutomationSuggestionActivity extends SettingsActivity { /* empty */ } public static class ZenModeAutomationSuggestionActivity extends SettingsActivity { /* empty */ }
public static class FingerprintSuggestionActivity extends FingerprintSettings { /* empty */ } public static class FingerprintSuggestionActivity extends FingerprintSettings { /* empty */ }

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2017 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;
import android.Manifest;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import java.util.List;
/**
* Connects app op info to the ApplicationsState. Wraps around the generic AppStateBaseBridge
* class to tailor to the semantics of {@link AppOpsManager#OP_REQUEST_INSTALL_PACKAGES}
* Also provides app filters that can use the info.
*/
public class AppStateInstallAppsBridge extends AppStateBaseBridge {
private static final String TAG = AppStateInstallAppsBridge.class.getSimpleName();
private final IPackageManager mIpm;
private final AppOpsManager mAppOpsManager;
private final Context mContext;
public AppStateInstallAppsBridge(Context context, ApplicationsState appState,
Callback callback) {
super(appState, callback);
mContext = context;
mIpm = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
@Override
protected void updateExtraInfo(AppEntry app, String packageName, int uid) {
app.extraInfo = createInstallAppsStateFor(packageName, uid);
}
@Override
protected void loadAllExtraInfo() {
// TODO: consider making this a batch operation with a single binder call
final List<AppEntry> allApps = mAppSession.getAllApps();
for (int i = 0; i < allApps.size(); i++) {
AppEntry currentEntry = allApps.get(i);
updateExtraInfo(currentEntry, currentEntry.info.packageName, currentEntry.info.uid);
}
}
private boolean hasRequestedAppOpPermission(String permission, String packageName) {
try {
String[] packages = mIpm.getAppOpPermissionPackages(permission);
return ArrayUtils.contains(packages, packageName);
} catch (RemoteException exc) {
Log.e(TAG, "PackageManager dead. Cannot get permission info");
return false;
}
}
private boolean hasPermission(String permission, int uid) {
try {
int result = mIpm.checkUidPermission(permission, uid);
return result == PackageManager.PERMISSION_GRANTED;
} catch (RemoteException e) {
Log.e(TAG, "PackageManager dead. Cannot get permission info");
return false;
}
}
private int getAppOpMode(int appOpCode, int uid, String packageName) {
return mAppOpsManager.checkOpNoThrow(appOpCode, uid, packageName);
}
InstallAppsState createInstallAppsStateFor(String packageName, int uid) {
final InstallAppsState appState = new InstallAppsState();
appState.permissionRequested = hasRequestedAppOpPermission(
Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName);
appState.permissionGranted = hasPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES,
uid);
appState.appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
packageName);
return appState;
}
/**
* Collection of information to be used as {@link AppEntry#extraInfo} objects
*/
public static class InstallAppsState {
boolean permissionRequested;
boolean permissionGranted;
int appOpMode;
public InstallAppsState() {
this.appOpMode = AppOpsManager.MODE_DEFAULT;
}
public boolean canInstallApps() {
if (appOpMode == AppOpsManager.MODE_DEFAULT) {
return permissionGranted;
} else {
return appOpMode == AppOpsManager.MODE_ALLOWED;
}
}
public int getSummary() {
return canInstallApps() ? R.string.external_source_trusted
: R.string.external_source_untrusted;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[permissionGranted: " + permissionGranted);
sb.append(", permissionRequested: " + permissionRequested);
sb.append(", appOpMode: " + appOpMode);
sb.append("]");
return sb.toString();
}
}
static final AppFilter FILTER_APP_SOURCES = new AppFilter() {
@Override
public void init() {
}
@Override
public boolean filterApp(AppEntry info) {
if (info.extraInfo == null || !(info.extraInfo instanceof InstallAppsState)) {
return false;
}
InstallAppsState state = (InstallAppsState) info.extraInfo;
return (state.appOpMode != AppOpsManager.MODE_DEFAULT) || state.permissionRequested;
}
};
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2017 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;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.Context;
import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import com.android.settings.R;
import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
public class ExternalSourcesDetails extends AppInfoWithHeader
implements OnPreferenceChangeListener {
private static final String KEY_EXTERNAL_SOURCES_SETTINGS_SWITCH =
"external_sources_settings_switch";
private static final String KEY_EXTERNAL_SOURCES_SETTINGS_DESC =
"external_sources_settings_description";
private AppStateInstallAppsBridge mAppBridge;
private AppOpsManager mAppOpsManager;
private SwitchPreference mSwitchPref;
private Preference mExternalSourcesSettingsDesc;
private InstallAppsState mInstallAppsState;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = getActivity();
mAppBridge = new AppStateInstallAppsBridge(context, mState, null);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
addPreferencesFromResource(R.xml.external_sources_details);
mSwitchPref = (SwitchPreference) findPreference(KEY_EXTERNAL_SOURCES_SETTINGS_SWITCH);
mExternalSourcesSettingsDesc = findPreference(KEY_EXTERNAL_SOURCES_SETTINGS_DESC);
getPreferenceScreen().setTitle(R.string.install_other_apps);
mSwitchPref.setTitle(R.string.external_source_switch_title);
mExternalSourcesSettingsDesc.setSummary(R.string.install_all_warning);
mSwitchPref.setOnPreferenceChangeListener(this);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean checked = (Boolean) newValue;
if (preference == mSwitchPref) {
if (mInstallAppsState != null && checked != mInstallAppsState.canInstallApps()) {
setCanInstallApps(checked);
refreshUi();
}
return true;
}
return false;
}
private void setCanInstallApps(boolean newState) {
mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
mPackageInfo.applicationInfo.uid, mPackageName,
newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
}
@Override
protected boolean refreshUi() {
mInstallAppsState = mAppBridge.createInstallAppsStateFor(mPackageName,
mPackageInfo.applicationInfo.uid);
final boolean canWrite = mInstallAppsState.canInstallApps();
mSwitchPref.setChecked(canWrite);
return true;
}
@Override
protected AlertDialog createDialog(int id, int errorCode) {
return null;
}
@Override
public int getMetricsCategory() {
return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
}
}

View File

@@ -58,6 +58,7 @@ import com.android.settings.AppHeader;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Settings.AllApplicationsActivity; import com.android.settings.Settings.AllApplicationsActivity;
import com.android.settings.Settings.HighPowerApplicationsActivity; import com.android.settings.Settings.HighPowerApplicationsActivity;
import com.android.settings.Settings.ManageExternalSourcesActivity;
import com.android.settings.Settings.NotificationAppListActivity; import com.android.settings.Settings.NotificationAppListActivity;
import com.android.settings.Settings.OverlaySettingsActivity; import com.android.settings.Settings.OverlaySettingsActivity;
import com.android.settings.Settings.StorageUseActivity; import com.android.settings.Settings.StorageUseActivity;
@@ -66,6 +67,7 @@ import com.android.settings.Settings.WriteSettingsActivity;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
import com.android.settings.applications.AppStateUsageBridge.UsageState; import com.android.settings.applications.AppStateUsageBridge.UsageState;
import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.dashboard.SummaryLoader; import com.android.settings.dashboard.SummaryLoader;
@@ -136,6 +138,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
public static final int FILTER_APPS_USAGE_ACCESS = 8; public static final int FILTER_APPS_USAGE_ACCESS = 8;
public static final int FILTER_APPS_WITH_OVERLAY = 9; public static final int FILTER_APPS_WITH_OVERLAY = 9;
public static final int FILTER_APPS_WRITE_SETTINGS = 10; public static final int FILTER_APPS_WRITE_SETTINGS = 10;
public static final int FILTER_APPS_INSTALL_SOURCES = 12;
// This is the string labels for the filter modes above, the order must be kept in sync. // This is the string labels for the filter modes above, the order must be kept in sync.
public static final int[] FILTER_LABELS = new int[]{ public static final int[] FILTER_LABELS = new int[]{
@@ -151,6 +154,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
R.string.filter_all_apps, // Usage access screen, never displayed R.string.filter_all_apps, // Usage access screen, never displayed
R.string.filter_overlay_apps, // Apps with overlay permission R.string.filter_overlay_apps, // Apps with overlay permission
R.string.filter_write_settings_apps, // Apps that can write system settings R.string.filter_write_settings_apps, // Apps that can write system settings
R.string.filter_install_sources_apps, // Apps that are trusted sources of apks
}; };
// This is the actual mapping to filters from FILTER_ constants above, the order must // This is the actual mapping to filters from FILTER_ constants above, the order must
// be kept in sync. // be kept in sync.
@@ -169,6 +173,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW, // Apps that can draw overlays AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW, // Apps that can draw overlays
AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS, // Apps that can write system settings AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS, // Apps that can write system settings
AppStateInstallAppsBridge.FILTER_APP_SOURCES,
}; };
// sort order // sort order
@@ -210,6 +215,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment
public static final int LIST_TYPE_HIGH_POWER = 5; public static final int LIST_TYPE_HIGH_POWER = 5;
public static final int LIST_TYPE_OVERLAY = 6; public static final int LIST_TYPE_OVERLAY = 6;
public static final int LIST_TYPE_WRITE_SETTINGS = 7; public static final int LIST_TYPE_WRITE_SETTINGS = 7;
public static final int LIST_TYPE_MANAGE_SOURCES = 8;
private View mRootView; private View mRootView;
@@ -259,6 +265,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment
mListType = LIST_TYPE_OVERLAY; mListType = LIST_TYPE_OVERLAY;
} else if (className.equals(WriteSettingsActivity.class.getName())) { } else if (className.equals(WriteSettingsActivity.class.getName())) {
mListType = LIST_TYPE_WRITE_SETTINGS; mListType = LIST_TYPE_WRITE_SETTINGS;
} else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
mListType = LIST_TYPE_MANAGE_SOURCES;
} else { } else {
mListType = LIST_TYPE_MAIN; mListType = LIST_TYPE_MAIN;
} }
@@ -379,6 +387,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment
return FILTER_APPS_WITH_OVERLAY; return FILTER_APPS_WITH_OVERLAY;
case LIST_TYPE_WRITE_SETTINGS: case LIST_TYPE_WRITE_SETTINGS:
return FILTER_APPS_WRITE_SETTINGS; return FILTER_APPS_WRITE_SETTINGS;
case LIST_TYPE_MANAGE_SOURCES:
return FILTER_APPS_INSTALL_SOURCES;
default: default:
return FILTER_APPS_ALL; return FILTER_APPS_ALL;
} }
@@ -412,6 +422,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment
return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS; return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_WRITE_SETTINGS: case LIST_TYPE_WRITE_SETTINGS:
return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS; return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_MANAGE_SOURCES:
return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
default: default:
return MetricsEvent.VIEW_UNKNOWN; return MetricsEvent.VIEW_UNKNOWN;
} }
@@ -503,6 +515,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment
case LIST_TYPE_WRITE_SETTINGS: case LIST_TYPE_WRITE_SETTINGS:
startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings); startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
break; break;
case LIST_TYPE_MANAGE_SOURCES:
startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps);
break;
// TODO: Figure out if there is a way where we can spin up the profile's settings // TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed app. // process ahead of time, to avoid a long load of data when user clicks on a managed app.
// Maybe when they load the list of apps that contains managed profile apps. // Maybe when they load the list of apps that contains managed profile apps.
@@ -796,6 +811,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment
mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this); mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) { } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this); mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) {
mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this);
} else { } else {
mExtraInfoBridge = null; mExtraInfoBridge = null;
} }
@@ -1206,6 +1223,11 @@ public class ManageApplications extends InstrumentedPreferenceFragment
holder.entry)); holder.entry));
break; break;
case LIST_TYPE_MANAGE_SOURCES:
holder.summary
.setText(((InstallAppsState) holder.entry.extraInfo).getSummary());
break;
default: default:
holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
break; break;