Home | History | Annotate | Download | only in applications
      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
     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
     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
    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 
    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 
    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
    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
    155     protected AlertDialog createDialog(int id, int errorCode) {
    156         return null;
    157     }
    158 
    159     @Override
    160     protected int getMetricsCategory() {
    161         return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
    162     }
    163 
    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 
    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 
    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