1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.settings.applications.appinfo;
17 
18 import static android.app.Activity.RESULT_CANCELED;
19 import static android.app.Activity.RESULT_OK;
20 
21 import android.app.ActivityManager;
22 import android.app.AppOpsManager;
23 import android.app.settings.SettingsEnums;
24 import android.content.Context;
25 import android.os.Bundle;
26 import android.os.UserHandle;
27 import android.os.UserManager;
28 
29 import androidx.appcompat.app.AlertDialog;
30 import androidx.preference.Preference;
31 import androidx.preference.Preference.OnPreferenceChangeListener;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.settings.R;
35 import com.android.settings.Settings;
36 import com.android.settings.applications.AppInfoWithHeader;
37 import com.android.settings.applications.AppStateInstallAppsBridge;
38 import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
39 import com.android.settingslib.RestrictedSwitchPreference;
40 import com.android.settingslib.applications.ApplicationsState.AppEntry;
41 
42 public class ExternalSourcesDetails extends AppInfoWithHeader
43         implements OnPreferenceChangeListener {
44 
45     private static final String KEY_EXTERNAL_SOURCE_SWITCH = "external_sources_settings_switch";
46 
47     private AppStateInstallAppsBridge mAppBridge;
48     private AppOpsManager mAppOpsManager;
49     private ActivityManager mActivityManager;
50     private UserManager mUserManager;
51     private RestrictedSwitchPreference mSwitchPref;
52     private InstallAppsState mInstallAppsState;
53 
54     @Override
onCreate(Bundle savedInstanceState)55     public void onCreate(Bundle savedInstanceState) {
56         super.onCreate(savedInstanceState);
57 
58         final Context context = getActivity();
59         mAppBridge = new AppStateInstallAppsBridge(context, mState, null);
60         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
61         mActivityManager = context.getSystemService(ActivityManager.class);
62         mUserManager = UserManager.get(context);
63 
64         addPreferencesFromResource(R.xml.external_sources_details);
65         mSwitchPref = (RestrictedSwitchPreference) findPreference(KEY_EXTERNAL_SOURCE_SWITCH);
66         mSwitchPref.setOnPreferenceChangeListener(this);
67     }
68 
69     @Override
onPreferenceChange(Preference preference, Object newValue)70     public boolean onPreferenceChange(Preference preference, Object newValue) {
71         final boolean checked = (Boolean) newValue;
72         if (preference == mSwitchPref) {
73             if (mInstallAppsState != null && checked != mInstallAppsState.canInstallApps()) {
74                 if (Settings.ManageAppExternalSourcesActivity.class.getName().equals(
75                         getIntent().getComponent().getClassName())) {
76                     setResult(checked ? RESULT_OK : RESULT_CANCELED);
77                 }
78                 setCanInstallApps(checked);
79                 refreshUi();
80             }
81             return true;
82         }
83         return false;
84     }
85 
getPreferenceSummary(Context context, AppEntry entry)86     public static CharSequence getPreferenceSummary(Context context, AppEntry entry) {
87         final UserHandle userHandle = UserHandle.getUserHandleForUid(entry.info.uid);
88         final UserManager um = UserManager.get(context);
89         final int userRestrictionSource = um.getUserRestrictionSource(
90                 UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userHandle)
91                 | um.getUserRestrictionSource(
92                         UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
93                         userHandle);
94         if ((userRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
95             return context.getString(R.string.disabled_by_admin);
96         } else if (userRestrictionSource != 0) {
97             return context.getString(R.string.disabled);
98         }
99         final InstallAppsState appsState = new AppStateInstallAppsBridge(context, null, null)
100                 .createInstallAppsStateFor(entry.info.packageName, entry.info.uid);
101         return context.getString(appsState.canInstallApps()
102                 ? R.string.app_permission_summary_allowed
103                 : R.string.app_permission_summary_not_allowed);
104     }
105 
106     @VisibleForTesting
setCanInstallApps(boolean newState)107     void setCanInstallApps(boolean newState) {
108         mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
109                 mPackageInfo.applicationInfo.uid, mPackageName,
110                 newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
111         if (!newState) {
112             killApp(mPackageInfo.applicationInfo.uid);
113         }
114     }
115 
killApp(int uid)116     private void killApp(int uid) {
117         if (UserHandle.isCore(uid)) {
118             return;
119         }
120         mActivityManager.killUid(uid, "User denied OP_REQUEST_INSTALL_PACKAGES");
121     }
122 
123     @Override
refreshUi()124     protected boolean refreshUi() {
125         if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
126             return false;
127         }
128         if (mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
129                 UserHandle.of(UserHandle.myUserId()))) {
130             mSwitchPref.setChecked(false);
131             mSwitchPref.setSummary(R.string.disabled);
132             mSwitchPref.setEnabled(false);
133             return true;
134         }
135         mSwitchPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
136         if (!mSwitchPref.isDisabledByAdmin()) {
137             mSwitchPref.checkRestrictionAndSetDisabled(
138                     UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
139         }
140         if (mSwitchPref.isDisabledByAdmin()) {
141             return true;
142         }
143         mInstallAppsState = mAppBridge.createInstallAppsStateFor(mPackageName,
144                 mPackageInfo.applicationInfo.uid);
145         if (!mInstallAppsState.isPotentialAppSource()) {
146             // Invalid app entry. Should not allow changing permission
147             mSwitchPref.setEnabled(false);
148             return true;
149         }
150         mSwitchPref.setChecked(mInstallAppsState.canInstallApps());
151         return true;
152     }
153 
154     @Override
createDialog(int id, int errorCode)155     protected AlertDialog createDialog(int id, int errorCode) {
156         return null;
157     }
158 
159     @Override
getMetricsCategory()160     public int getMetricsCategory() {
161         return SettingsEnums.MANAGE_EXTERNAL_SOURCES;
162     }
163 }
164