1 /*
2  * Copyright (C) 2015 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;
17 
18 import android.app.AppOpsManager;
19 import android.app.admin.DevicePolicyManager;
20 import android.app.settings.SettingsEnums;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.os.Bundle;
27 import android.provider.Settings;
28 
29 import androidx.annotation.VisibleForTesting;
30 import androidx.appcompat.app.AlertDialog;
31 import androidx.preference.Preference;
32 import androidx.preference.Preference.OnPreferenceChangeListener;
33 import androidx.preference.Preference.OnPreferenceClickListener;
34 import androidx.preference.SwitchPreference;
35 
36 import com.android.settings.R;
37 import com.android.settings.applications.AppStateUsageBridge.UsageState;
38 import com.android.settings.overlay.FeatureFactory;
39 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
40 
41 public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
42         OnPreferenceClickListener {
43 
44     private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
45     private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
46     private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
47 
48     // Use a bridge to get the usage stats but don't initialize it to connect with all state.
49     // TODO: Break out this functionality into its own class.
50     private AppStateUsageBridge mUsageBridge;
51     private AppOpsManager mAppOpsManager;
52     private SwitchPreference mSwitchPref;
53     private Preference mUsageDesc;
54     private Intent mSettingsIntent;
55     private UsageState mUsageState;
56     private DevicePolicyManager mDpm;
57 
58     @Override
onCreate(Bundle savedInstanceState)59     public void onCreate(Bundle savedInstanceState) {
60         super.onCreate(savedInstanceState);
61 
62         Context context = getActivity();
63         mUsageBridge = new AppStateUsageBridge(context, mState, null);
64         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
65         mDpm = context.getSystemService(DevicePolicyManager.class);
66 
67         addPreferencesFromResource(R.xml.app_ops_permissions_details);
68         mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
69         mUsageDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
70 
71         getPreferenceScreen().setTitle(R.string.usage_access);
72         mSwitchPref.setTitle(R.string.permit_usage_access);
73         mUsageDesc.setSummary(R.string.usage_access_description);
74 
75         mSwitchPref.setOnPreferenceChangeListener(this);
76 
77         mSettingsIntent = new Intent(Intent.ACTION_MAIN)
78                 .addCategory(Settings.INTENT_CATEGORY_USAGE_ACCESS_CONFIG)
79                 .setPackage(mPackageName);
80     }
81 
82     @Override
onPreferenceClick(Preference preference)83     public boolean onPreferenceClick(Preference preference) {
84         return false;
85     }
86 
87     @Override
onPreferenceChange(Preference preference, Object newValue)88     public boolean onPreferenceChange(Preference preference, Object newValue) {
89         if (preference == mSwitchPref) {
90             if (mUsageState != null && (Boolean) newValue != mUsageState.isPermissible()) {
91                 if (mUsageState.isPermissible() && mDpm.isProfileOwnerApp(mPackageName)) {
92                     new AlertDialog.Builder(getContext())
93                             .setIcon(com.android.internal.R.drawable.ic_dialog_alert_material)
94                             .setTitle(android.R.string.dialog_alert_title)
95                             .setMessage(R.string.work_profile_usage_access_warning)
96                             .setPositiveButton(R.string.okay, null)
97                             .show();
98                 }
99                 setHasAccess(!mUsageState.isPermissible());
100                 refreshUi();
101             }
102             return true;
103         }
104         return false;
105     }
106 
setHasAccess(boolean newState)107     private void setHasAccess(boolean newState) {
108         logSpecialPermissionChange(newState, mPackageName);
109         mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS, mPackageInfo.applicationInfo.uid,
110                 mPackageName, newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
111     }
112 
113     @VisibleForTesting
logSpecialPermissionChange(boolean newState, String packageName)114     void logSpecialPermissionChange(boolean newState, String packageName) {
115         int logCategory = newState ? SettingsEnums.APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW
116                 : SettingsEnums.APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY;
117         final MetricsFeatureProvider metricsFeatureProvider =
118                 FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider();
119         metricsFeatureProvider.action(
120                 metricsFeatureProvider.getAttribution(getActivity()),
121                 logCategory,
122                 getMetricsCategory(),
123                 packageName,
124                 0);
125     }
126 
127     @Override
refreshUi()128     protected boolean refreshUi() {
129         retrieveAppEntry();
130         if (mAppEntry == null) {
131             return false;
132         }
133         if (mPackageInfo == null) {
134             return false; // onCreate must have failed, make sure to exit
135         }
136         mUsageState = mUsageBridge.getUsageInfo(mPackageName,
137                 mPackageInfo.applicationInfo.uid);
138 
139         boolean hasAccess = mUsageState.isPermissible();
140         mSwitchPref.setChecked(hasAccess);
141         mSwitchPref.setEnabled(mUsageState.permissionDeclared);
142 
143         ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
144                 PackageManager.GET_META_DATA, mUserId);
145         if (resolveInfo != null) {
146             Bundle metaData = resolveInfo.activityInfo.metaData;
147             mSettingsIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
148                     resolveInfo.activityInfo.name));
149             if (metaData != null
150                     && metaData.containsKey(Settings.METADATA_USAGE_ACCESS_REASON)) {
151                 mSwitchPref.setSummary(
152                         metaData.getString(Settings.METADATA_USAGE_ACCESS_REASON));
153             }
154         }
155 
156         return true;
157     }
158 
159     @Override
createDialog(int id, int errorCode)160     protected AlertDialog createDialog(int id, int errorCode) {
161         return null;
162     }
163 
164     @Override
getMetricsCategory()165     public int getMetricsCategory() {
166         return SettingsEnums.APPLICATIONS_USAGE_ACCESS_DETAIL;
167     }
168 
169 }
170