AppPermissionsFragmentWear.java revision 1019d73896ca8ec9baac1492c08443c7a6339380
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
17package com.android.packageinstaller.permission.ui.wear;
18
19import android.Manifest;
20import android.app.Activity;
21import android.app.Fragment;
22import android.content.Intent;
23import android.content.pm.PackageInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.PermissionInfo;
26import android.os.Bundle;
27import android.os.UserHandle;
28import android.preference.Preference;
29import android.preference.PreferenceFragment;
30import android.preference.PreferenceScreen;
31import android.preference.SwitchPreference;
32import android.support.wearable.view.WearableDialogHelper;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.widget.Toast;
38
39import com.android.packageinstaller.R;
40import com.android.packageinstaller.permission.model.AppPermissionGroup;
41import com.android.packageinstaller.permission.model.AppPermissions;
42import com.android.packageinstaller.permission.model.Permission;
43import com.android.packageinstaller.permission.utils.LocationUtils;
44import com.android.packageinstaller.permission.utils.SafetyNetLogger;
45import com.android.packageinstaller.permission.utils.Utils;
46import com.android.settingslib.RestrictedLockUtils;
47import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
48
49import java.util.ArrayList;
50import java.util.List;
51
52public final class AppPermissionsFragmentWear extends PreferenceFragment {
53    private static final String LOG_TAG = "AppPermFragWear";
54
55    private static final String KEY_NO_PERMISSIONS = "no_permissions";
56
57    public static AppPermissionsFragmentWear newInstance(String packageName) {
58        return setPackageName(new AppPermissionsFragmentWear(), packageName);
59    }
60
61    private static <T extends Fragment> T setPackageName(T fragment, String packageName) {
62        Bundle arguments = new Bundle();
63        arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
64        fragment.setArguments(arguments);
65        return fragment;
66    }
67
68    private PackageManager mPackageManager;
69    private List<AppPermissionGroup> mToggledGroups;
70    private AppPermissions mAppPermissions;
71
72    private boolean mHasConfirmedRevoke;
73
74    /**
75     * Provides click behavior for disabled preferences.
76     * We can't use {@link PreferenceFragment#onPreferenceTreeClick}, as the base
77     * {@link SwitchPreference} doesn't delegate to that method if the preference is disabled.
78     */
79    private static class PermissionSwitchPreference extends SwitchPreference {
80
81        private final Activity mActivity;
82
83        public PermissionSwitchPreference(Activity activity) {
84            super(activity);
85            this.mActivity = activity;
86        }
87
88        @Override
89        public void performClick(PreferenceScreen preferenceScreen) {
90            super.performClick(preferenceScreen);
91            if (!isEnabled()) {
92                // If setting the permission is disabled, it must have been locked
93                // by the device or profile owner. So get that info and pass it to
94                // the support details dialog.
95                EnforcedAdmin deviceOrProfileOwner = RestrictedLockUtils.getProfileOrDeviceOwner(
96                    mActivity, UserHandle.myUserId());
97                RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
98                    mActivity, deviceOrProfileOwner);
99            }
100        }
101    }
102
103    @Override
104    public void onCreate(Bundle savedInstanceState) {
105        super.onCreate(savedInstanceState);
106
107        String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
108        Activity activity = getActivity();
109        mPackageManager = activity.getPackageManager();
110
111        PackageInfo packageInfo;
112
113        try {
114            packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
115        } catch (PackageManager.NameNotFoundException e) {
116            Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
117            packageInfo = null;
118        }
119
120        if (packageInfo == null) {
121            Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
122            activity.finish();
123            return;
124        }
125
126        mAppPermissions = new AppPermissions(
127                activity, packageInfo, null, true, () -> getActivity().finish());
128
129        addPreferencesFromResource(R.xml.watch_permissions);
130        initializePermissionGroupList();
131    }
132
133    @Override
134    public void onResume() {
135        super.onResume();
136        mAppPermissions.refresh();
137
138        // Also refresh the UI
139        for (final AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
140            Preference pref = findPreference(group.getName());
141            if (pref instanceof SwitchPreference) {
142                ((SwitchPreference) pref).setChecked(group.areRuntimePermissionsGranted());
143            }
144        }
145    }
146
147    @Override
148    public void onPause() {
149        super.onPause();
150        logAndClearToggledGroups();
151    }
152
153    private void initializePermissionGroupList() {
154        final String packageName = mAppPermissions.getPackageInfo().packageName;
155        List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups();
156        List<SwitchPreference> nonSystemPreferences = new ArrayList<>();
157
158        if (!groups.isEmpty()) {
159            getPreferenceScreen().removePreference(findPreference(KEY_NO_PERMISSIONS));
160        }
161
162        for (final AppPermissionGroup group : groups) {
163            if (!Utils.shouldShowPermission(group, packageName)) {
164                continue;
165            }
166
167            boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
168
169            if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) {
170                // If permission is controlled individually, we show all requested permission
171                // inside this group.
172                for (PermissionInfo perm : getPermissionInfosFromGroup(group)) {
173                    final SwitchPreference pref = createSwitchPreferenceForPermission(group, perm);
174                    showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
175                }
176            } else {
177                final SwitchPreference pref = createSwitchPreferenceForGroup(group);
178                showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
179            }
180        }
181
182        // Now add the non-system settings to the end of the list
183        for (SwitchPreference nonSystemPreference : nonSystemPreferences) {
184            getPreferenceScreen().addPreference(nonSystemPreference);
185        }
186    }
187
188    private void showOrAddToNonSystemPreferences(SwitchPreference pref,
189            List<SwitchPreference> nonSystemPreferences, // Mutate
190            boolean isPlatform) {
191        // The UI shows System settings first, then non-system settings
192        if (isPlatform) {
193            getPreferenceScreen().addPreference(pref);
194        } else {
195            nonSystemPreferences.add(pref);
196        }
197    }
198
199    private SwitchPreference createSwitchPreferenceForPermission(AppPermissionGroup group,
200            PermissionInfo perm) {
201        final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
202        pref.setKey(perm.name);
203        pref.setTitle(perm.loadLabel(mPackageManager));
204        pref.setChecked(group.areRuntimePermissionsGranted(new String[]{ perm.name }));
205        pref.setOnPreferenceChangeListener((p, newVal) -> {
206            if((Boolean) newVal) {
207                group.grantRuntimePermissions(false, new String[]{ perm.name });
208            } else {
209                group.revokeRuntimePermissions(true, new String[]{ perm.name });
210            }
211            return true;
212        });
213        return pref;
214    }
215
216    private SwitchPreference createSwitchPreferenceForGroup(AppPermissionGroup group) {
217        final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
218
219        pref.setKey(group.getName());
220        pref.setTitle(group.getLabel());
221        pref.setChecked(group.areRuntimePermissionsGranted());
222
223        if (group.isPolicyFixed()) {
224            pref.setEnabled(false);
225        } else {
226            pref.setOnPreferenceChangeListener((p, newVal) -> {
227                if (LocationUtils.isLocationGroupAndProvider(
228                        group.getName(), group.getApp().packageName)) {
229                    LocationUtils.showLocationDialog(
230                            getContext(), mAppPermissions.getAppLabel());
231                    return false;
232                }
233
234                if ((Boolean) newVal) {
235                    setPermission(group, pref, true);
236                } else {
237                    final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
238                    if (grantedByDefault
239                            || (!group.hasRuntimePermission() && !mHasConfirmedRevoke)) {
240                        new WearableDialogHelper.DialogBuilder(getContext())
241                                .setNegativeIcon(R.drawable.confirm_button)
242                                .setPositiveIcon(R.drawable.cancel_button)
243                                .setNegativeButton(R.string.grant_dialog_button_deny_anyway,
244                                        (dialog, which) -> {
245                                            setPermission(group, pref, false);
246                                            if (!group.hasGrantedByDefaultPermission()) {
247                                                mHasConfirmedRevoke = true;
248                                            }
249                                        })
250                                .setPositiveButton(R.string.cancel, (dialog, which) -> {})
251                                .setMessage(grantedByDefault ?
252                                        R.string.system_warning : R.string.old_sdk_deny_warning)
253                                .show();
254                        return false;
255                    } else {
256                        setPermission(group, pref, false);
257                    }
258                }
259
260                return true;
261            });
262        }
263        return pref;
264    }
265
266    private void setPermission(AppPermissionGroup group, SwitchPreference pref, boolean grant) {
267        if (grant) {
268            group.grantRuntimePermissions(false);
269        } else {
270            group.revokeRuntimePermissions(false);
271        }
272        addToggledGroup(group);
273        pref.setChecked(grant);
274    }
275
276    private void addToggledGroup(AppPermissionGroup group) {
277        if (mToggledGroups == null) {
278            mToggledGroups = new ArrayList<>();
279        }
280        // Double toggle is back to initial state.
281        if (mToggledGroups.contains(group)) {
282            mToggledGroups.remove(group);
283        } else {
284            mToggledGroups.add(group);
285        }
286    }
287
288    private void logAndClearToggledGroups() {
289        if (mToggledGroups != null) {
290            String packageName = mAppPermissions.getPackageInfo().packageName;
291            SafetyNetLogger.logPermissionsToggled(packageName, mToggledGroups);
292            mToggledGroups = null;
293        }
294    }
295
296    private List<PermissionInfo> getPermissionInfosFromGroup(AppPermissionGroup group) {
297        ArrayList<PermissionInfo> permInfos = new ArrayList<>(group.getPermissions().size());
298        for(Permission perm : group.getPermissions()) {
299            try {
300                permInfos.add(mPackageManager.getPermissionInfo(perm.getName(), 0));
301            } catch (PackageManager.NameNotFoundException e) {
302                Log.w(LOG_TAG, "No permission:" + perm.getName());
303            }
304        }
305        return permInfos;
306    }
307}
308