• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AlertDialog;
19 import android.app.AppOpsManager;
20 import android.content.ActivityNotFoundException;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.os.Bundle;
28 import android.os.UserHandle;
29 import android.preference.Preference;
30 import android.preference.Preference.OnPreferenceChangeListener;
31 import android.preference.Preference.OnPreferenceClickListener;
32 import android.preference.SwitchPreference;
33 import android.provider.Settings;
34 import android.util.Log;
35 
36 import com.android.internal.logging.MetricsLogger;
37 import com.android.settings.InstrumentedFragment;
38 import com.android.settings.R;
39 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
40 import com.android.settings.applications.AppStateOverlayBridge.OverlayState;
41 import com.android.settingslib.applications.ApplicationsState;
42 import com.android.settingslib.applications.ApplicationsState.AppEntry;
43 
44 import java.util.List;
45 
46 public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
47         OnPreferenceClickListener {
48 
49     private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
50     private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
51     private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
52     private static final String LOG_TAG = "DrawOverlayDetails";
53 
54     private static final int [] APP_OPS_OP_CODE = {
55             AppOpsManager.OP_SYSTEM_ALERT_WINDOW
56     };
57 
58     // Use a bridge to get the overlay details but don't initialize it to connect with all state.
59     // TODO: Break out this functionality into its own class.
60     private AppStateOverlayBridge mOverlayBridge;
61     private AppOpsManager mAppOpsManager;
62     private SwitchPreference mSwitchPref;
63     private Preference mOverlayPrefs;
64     private Preference mOverlayDesc;
65     private Intent mSettingsIntent;
66     private OverlayState mOverlayState;
67 
68     @Override
onCreate(Bundle savedInstanceState)69     public void onCreate(Bundle savedInstanceState) {
70         super.onCreate(savedInstanceState);
71 
72         Context context = getActivity();
73         mOverlayBridge = new AppStateOverlayBridge(context, mState, null);
74         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
75 
76         // find preferences
77         addPreferencesFromResource(R.xml.app_ops_permissions_details);
78         mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
79         mOverlayPrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
80         mOverlayDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
81 
82         // set title/summary for all of them
83         getPreferenceScreen().setTitle(R.string.draw_overlay);
84         mSwitchPref.setTitle(R.string.permit_draw_overlay);
85         mOverlayPrefs.setTitle(R.string.app_overlay_permission_preference);
86         mOverlayDesc.setSummary(R.string.allow_overlay_description);
87 
88         // install event listeners
89         mSwitchPref.setOnPreferenceChangeListener(this);
90         mOverlayPrefs.setOnPreferenceClickListener(this);
91 
92         mSettingsIntent = new Intent(Intent.ACTION_MAIN)
93                 .setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
94     }
95 
96     @Override
onPreferenceClick(Preference preference)97     public boolean onPreferenceClick(Preference preference) {
98         if (preference == mOverlayPrefs) {
99             if (mSettingsIntent != null) {
100                 try {
101                     getActivity().startActivityAsUser(mSettingsIntent, new UserHandle(mUserId));
102                 } catch (ActivityNotFoundException e) {
103                     Log.w(LOG_TAG, "Unable to launch app draw overlay settings " + mSettingsIntent, e);
104                 }
105             }
106             return true;
107         }
108         return false;
109     }
110 
111     @Override
onPreferenceChange(Preference preference, Object newValue)112     public boolean onPreferenceChange(Preference preference, Object newValue) {
113         if (preference == mSwitchPref) {
114             if (mOverlayState != null && (Boolean) newValue != mOverlayState.isPermissible()) {
115                 setCanDrawOverlay(!mOverlayState.isPermissible());
116                 refreshUi();
117             }
118             return true;
119         }
120         return false;
121     }
122 
setCanDrawOverlay(boolean newState)123     private void setCanDrawOverlay(boolean newState) {
124         mAppOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
125                 mPackageInfo.applicationInfo.uid, mPackageName, newState
126                 ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
127     }
128 
canDrawOverlay(String pkgName)129     private boolean canDrawOverlay(String pkgName) {
130         int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
131                 mPackageInfo.applicationInfo.uid, pkgName);
132         if (result == AppOpsManager.MODE_ALLOWED) {
133             return true;
134         }
135 
136         return false;
137     }
138 
139     @Override
refreshUi()140     protected boolean refreshUi() {
141         mOverlayState = mOverlayBridge.getOverlayInfo(mPackageName,
142                 mPackageInfo.applicationInfo.uid);
143 
144         boolean isAllowed = mOverlayState.isPermissible();
145         mSwitchPref.setChecked(isAllowed);
146         // you cannot ask a user to grant you a permission you did not have!
147         mSwitchPref.setEnabled(mOverlayState.permissionDeclared);
148         mOverlayPrefs.setEnabled(isAllowed);
149         getPreferenceScreen().removePreference(mOverlayPrefs);
150 
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     protected int getMetricsCategory() {
161         return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
162     }
163 
getSummary(Context context, AppEntry entry)164     public static CharSequence getSummary(Context context, AppEntry entry) {
165         if (entry.extraInfo != null) {
166             return getSummary(context, new OverlayState((PermissionState)entry.extraInfo));
167         }
168 
169         // fallback if for whatever reason entry.extrainfo is null - the result
170         // may be less accurate
171         return getSummary(context, entry.info.packageName);
172     }
173 
getSummary(Context context, OverlayState overlayState)174     public static CharSequence getSummary(Context context, OverlayState overlayState) {
175         return context.getString(overlayState.isPermissible() ?
176             R.string.system_alert_window_on : R.string.system_alert_window_off);
177     }
178 
getSummary(Context context, String pkg)179     public static CharSequence getSummary(Context context, String pkg) {
180         // first check if pkg is a system pkg
181         PackageManager packageManager = context.getPackageManager();
182         int uid = -1;
183         try {
184             ApplicationInfo appInfo = packageManager.getApplicationInfo(pkg, 0);
185             uid = appInfo.uid;
186             if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
187                 return context.getString(R.string.system_alert_window_on);
188             }
189         } catch (PackageManager.NameNotFoundException e) {
190             // pkg doesn't even exist?
191             Log.w(LOG_TAG, "Package " + pkg + " not found", e);
192             return context.getString(R.string.system_alert_window_off);
193         }
194 
195         AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context
196                 .APP_OPS_SERVICE);
197         if (uid == -1) {
198             return context.getString(R.string.system_alert_window_off);
199         }
200 
201         int mode = appOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, pkg);
202         return context.getString((mode == AppOpsManager.MODE_ALLOWED) ?
203                 R.string.system_alert_window_on : R.string.system_alert_window_off);
204     }
205 }
206