AppPermissionsFragmentWear.java revision 9385a5c742c3989a15c3b5bf511ca77fa1b02113
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            if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) {
141                for (PermissionInfo perm : getPermissionInfosFromGroup(group)) {
142                    setPreferenceCheckedIfPresent(perm.name,
143                            group.areRuntimePermissionsGranted(new String[]{ perm.name }));
144                }
145            } else {
146                setPreferenceCheckedIfPresent(group.getName(), group.areRuntimePermissionsGranted());
147            }
148        }
149    }
150
151    @Override
152    public void onPause() {
153        super.onPause();
154        logAndClearToggledGroups();
155    }
156
157    private void initializePermissionGroupList() {
158        final String packageName = mAppPermissions.getPackageInfo().packageName;
159        List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups();
160        List<SwitchPreference> nonSystemPreferences = new ArrayList<>();
161
162        if (!groups.isEmpty()) {
163            getPreferenceScreen().removePreference(findPreference(KEY_NO_PERMISSIONS));
164        }
165
166        for (final AppPermissionGroup group : groups) {
167            if (!Utils.shouldShowPermission(group, packageName)) {
168                continue;
169            }
170
171            boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
172
173            if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) {
174                // If permission is controlled individually, we show all requested permission
175                // inside this group.
176                for (PermissionInfo perm : getPermissionInfosFromGroup(group)) {
177                    final SwitchPreference pref = createSwitchPreferenceForPermission(group, perm);
178                    showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
179                }
180            } else {
181                final SwitchPreference pref = createSwitchPreferenceForGroup(group);
182                showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
183            }
184        }
185
186        // Now add the non-system settings to the end of the list
187        for (SwitchPreference nonSystemPreference : nonSystemPreferences) {
188            getPreferenceScreen().addPreference(nonSystemPreference);
189        }
190    }
191
192    private void showOrAddToNonSystemPreferences(SwitchPreference pref,
193            List<SwitchPreference> nonSystemPreferences, // Mutate
194            boolean isPlatform) {
195        // The UI shows System settings first, then non-system settings
196        if (isPlatform) {
197            getPreferenceScreen().addPreference(pref);
198        } else {
199            nonSystemPreferences.add(pref);
200        }
201    }
202
203    private SwitchPreference createSwitchPreferenceForPermission(AppPermissionGroup group,
204            PermissionInfo perm) {
205        final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
206        pref.setKey(perm.name);
207        pref.setTitle(perm.loadLabel(mPackageManager));
208        pref.setChecked(group.areRuntimePermissionsGranted(new String[]{ perm.name }));
209        pref.setOnPreferenceChangeListener((p, newVal) -> {
210            if((Boolean) newVal) {
211                group.grantRuntimePermissions(false, new String[]{ perm.name });
212            } else {
213                group.revokeRuntimePermissions(true, new String[]{ perm.name });
214            }
215            return true;
216        });
217        return pref;
218    }
219
220    private SwitchPreference createSwitchPreferenceForGroup(AppPermissionGroup group) {
221        final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
222
223        pref.setKey(group.getName());
224        pref.setTitle(group.getLabel());
225        pref.setChecked(group.areRuntimePermissionsGranted());
226
227        if (group.isPolicyFixed()) {
228            pref.setEnabled(false);
229        } else {
230            pref.setOnPreferenceChangeListener((p, newVal) -> {
231                if (LocationUtils.isLocationGroupAndProvider(
232                        group.getName(), group.getApp().packageName)) {
233                    LocationUtils.showLocationDialog(
234                            getContext(), mAppPermissions.getAppLabel());
235                    return false;
236                }
237
238                if ((Boolean) newVal) {
239                    setPermission(group, pref, true);
240                } else {
241                    final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
242                    if (grantedByDefault
243                            || (!group.doesSupportRuntimePermissions() && !mHasConfirmedRevoke)) {
244                        new WearableDialogHelper.DialogBuilder(getContext())
245                                .setNegativeIcon(R.drawable.confirm_button)
246                                .setPositiveIcon(R.drawable.cancel_button)
247                                .setNegativeButton(R.string.grant_dialog_button_deny_anyway,
248                                        (dialog, which) -> {
249                                            setPermission(group, pref, false);
250                                            if (!group.hasGrantedByDefaultPermission()) {
251                                                mHasConfirmedRevoke = true;
252                                            }
253                                        })
254                                .setPositiveButton(R.string.cancel, (dialog, which) -> {})
255                                .setMessage(grantedByDefault ?
256                                        R.string.system_warning : R.string.old_sdk_deny_warning)
257                                .show();
258                        return false;
259                    } else {
260                        setPermission(group, pref, false);
261                    }
262                }
263
264                return true;
265            });
266        }
267        return pref;
268    }
269
270    private void setPermission(AppPermissionGroup group, SwitchPreference pref, boolean grant) {
271        if (grant) {
272            group.grantRuntimePermissions(false);
273        } else {
274            group.revokeRuntimePermissions(false);
275        }
276        addToggledGroup(group);
277        pref.setChecked(grant);
278    }
279
280    private void addToggledGroup(AppPermissionGroup group) {
281        if (mToggledGroups == null) {
282            mToggledGroups = new ArrayList<>();
283        }
284        // Double toggle is back to initial state.
285        if (mToggledGroups.contains(group)) {
286            mToggledGroups.remove(group);
287        } else {
288            mToggledGroups.add(group);
289        }
290    }
291
292    private void logAndClearToggledGroups() {
293        if (mToggledGroups != null) {
294            String packageName = mAppPermissions.getPackageInfo().packageName;
295            SafetyNetLogger.logPermissionsToggled(packageName, mToggledGroups);
296            mToggledGroups = null;
297        }
298    }
299
300    private List<PermissionInfo> getPermissionInfosFromGroup(AppPermissionGroup group) {
301        ArrayList<PermissionInfo> permInfos = new ArrayList<>(group.getPermissions().size());
302        for(Permission perm : group.getPermissions()) {
303            try {
304                permInfos.add(mPackageManager.getPermissionInfo(perm.getName(), 0));
305            } catch (PackageManager.NameNotFoundException e) {
306                Log.w(LOG_TAG, "No permission:" + perm.getName());
307            }
308        }
309        return permInfos;
310    }
311
312    private void setPreferenceCheckedIfPresent(String preferenceKey, boolean checked) {
313        Preference pref = findPreference(preferenceKey);
314        if (pref instanceof SwitchPreference) {
315            ((SwitchPreference) pref).setChecked(checked);
316        }
317    }
318}
319