• 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 
17 package com.android.packageinstaller.permission.ui;
18 
19 import android.app.Activity;
20 
21 import android.app.DialogFragment;
22 import android.app.Fragment;
23 import android.content.Intent;
24 import android.content.IntentSender;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.graphics.drawable.Drawable;
28 import android.os.Bundle;
29 import android.os.RemoteCallback;
30 import android.preference.Preference;
31 import android.preference.PreferenceCategory;
32 import android.preference.PreferenceFragment;
33 import android.preference.PreferenceGroup;
34 import android.preference.PreferenceScreen;
35 import android.preference.SwitchPreference;
36 import android.preference.TwoStatePreference;
37 import android.text.SpannableString;
38 import android.text.TextUtils;
39 import android.text.style.ForegroundColorSpan;
40 import android.util.TypedValue;
41 import android.view.View;
42 import android.widget.Button;
43 import android.widget.ImageView;
44 import android.widget.TextView;
45 import com.android.packageinstaller.R;
46 import com.android.packageinstaller.permission.model.AppPermissionGroup;
47 import com.android.packageinstaller.permission.model.AppPermissions;
48 import com.android.packageinstaller.permission.utils.Utils;
49 import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment.OnActionConfirmedListener;
50 
51 import java.util.List;
52 
53 public final class ReviewPermissionsActivity extends Activity
54         implements OnActionConfirmedListener {
55 
56     @Override
onCreate(Bundle savedInstanceState)57     protected void onCreate(Bundle savedInstanceState) {
58         super.onCreate(savedInstanceState);
59 
60         PackageInfo packageInfo = getTargetPackageInfo();
61         if (packageInfo == null) {
62             finish();
63             return;
64         }
65 
66         setContentView(R.layout.review_permissions);
67         if (getFragmentManager().findFragmentById(R.id.preferences_frame) == null) {
68             getFragmentManager().beginTransaction().add(R.id.preferences_frame,
69                     ReviewPermissionsFragment.newInstance(packageInfo)).commit();
70         }
71     }
72 
73     @Override
onActionConfirmed(String action)74     public void onActionConfirmed(String action) {
75         Fragment fragment = getFragmentManager().findFragmentById(R.id.preferences_frame);
76         if (fragment instanceof OnActionConfirmedListener) {
77             ((OnActionConfirmedListener) fragment).onActionConfirmed(action);
78         }
79     }
80 
getTargetPackageInfo()81     private PackageInfo getTargetPackageInfo() {
82         String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
83         if (TextUtils.isEmpty(packageName)) {
84             return null;
85         }
86         try {
87             return getPackageManager().getPackageInfo(packageName,
88                     PackageManager.GET_PERMISSIONS);
89         } catch (PackageManager.NameNotFoundException e) {
90             return null;
91         }
92     }
93 
94     public static final class ReviewPermissionsFragment extends PreferenceFragment
95             implements View.OnClickListener, Preference.OnPreferenceChangeListener,
96             ConfirmActionDialogFragment.OnActionConfirmedListener {
97         public static final String EXTRA_PACKAGE_INFO =
98                 "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO";
99 
100         private AppPermissions mAppPermissions;
101 
102         private Button mContinueButton;
103         private Button mCancelButton;
104 
105         private PreferenceCategory mNewPermissionsCategory;
106 
107         private boolean mHasConfirmedRevoke;
108 
newInstance(PackageInfo packageInfo)109         public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) {
110             Bundle arguments = new Bundle();
111             arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo);
112             ReviewPermissionsFragment instance = new ReviewPermissionsFragment();
113             instance.setArguments(arguments);
114             instance.setRetainInstance(true);
115             return instance;
116         }
117 
118         @Override
onCreate(Bundle savedInstanceState)119         public void onCreate(Bundle savedInstanceState) {
120             super.onCreate(savedInstanceState);
121 
122             Activity activity = getActivity();
123             if (activity == null) {
124                 return;
125             }
126 
127             PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO);
128             if (packageInfo == null) {
129                 activity.finish();
130                 return;
131             }
132 
133             mAppPermissions = new AppPermissions(activity, packageInfo, null, false,
134                     new Runnable() {
135                         @Override
136                         public void run() {
137                             getActivity().finish();
138                         }
139                     });
140 
141             if (mAppPermissions.getPermissionGroups().isEmpty()) {
142                 activity.finish();
143                 return;
144             }
145 
146             boolean reviewRequired = false;
147             for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
148                 if (group.isReviewRequired()) {
149                     reviewRequired = true;
150                     break;
151                 }
152             }
153 
154             if (!reviewRequired) {
155                 activity.finish();
156             }
157         }
158 
159         @Override
onViewCreated(View view, Bundle savedInstanceState)160         public void onViewCreated(View view, Bundle savedInstanceState) {
161             super.onViewCreated(view, savedInstanceState);
162             bindUi();
163         }
164 
165         @Override
onResume()166         public void onResume() {
167             super.onResume();
168             mAppPermissions.refresh();
169             loadPreferences();
170         }
171 
172         @Override
onClick(View view)173         public void onClick(View view) {
174             Activity activity = getActivity();
175             if (activity == null) {
176                 return;
177             }
178             if (view == mContinueButton) {
179                 confirmPermissionsReview();
180                 executeCallback(true);
181             } else if (view == mCancelButton) {
182                 executeCallback(false);
183                 activity.setResult(Activity.RESULT_CANCELED);
184             }
185             activity.finish();
186         }
187 
188         @Override
onPreferenceChange(Preference preference, Object newValue)189         public boolean onPreferenceChange(Preference preference, Object newValue) {
190             if (mHasConfirmedRevoke) {
191                 return true;
192             }
193             if (preference instanceof SwitchPreference) {
194                 SwitchPreference switchPreference = (SwitchPreference) preference;
195                 if (switchPreference.isChecked()) {
196                     showWarnRevokeDialog(switchPreference.getKey());
197                 } else {
198                     return true;
199                 }
200             }
201             return false;
202         }
203 
204         @Override
onActionConfirmed(String action)205         public void onActionConfirmed(String action) {
206             Preference preference = getPreferenceManager().findPreference(action);
207             if (preference instanceof SwitchPreference) {
208                 SwitchPreference switchPreference = (SwitchPreference) preference;
209                 switchPreference.setChecked(false);
210                 mHasConfirmedRevoke = true;
211             }
212         }
213 
showWarnRevokeDialog(final String groupName)214         private void showWarnRevokeDialog(final String groupName) {
215             DialogFragment fragment = ConfirmActionDialogFragment.newInstance(
216                     getString(R.string.old_sdk_deny_warning), groupName);
217             fragment.show(getFragmentManager(), fragment.getClass().getName());
218         }
219 
confirmPermissionsReview()220         private void confirmPermissionsReview() {
221             PreferenceGroup preferenceGroup = mNewPermissionsCategory != null
222                 ? mNewPermissionsCategory : getPreferenceScreen();
223 
224             final int preferenceCount = preferenceGroup.getPreferenceCount();
225             for (int i = 0; i < preferenceCount; i++) {
226                 Preference preference = preferenceGroup.getPreference(i);
227                 if (preference instanceof TwoStatePreference) {
228                     TwoStatePreference twoStatePreference = (TwoStatePreference) preference;
229                     String groupName = preference.getKey();
230                     AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
231                     if (twoStatePreference.isChecked()) {
232                         group.grantRuntimePermissions(false);
233                     } else {
234                         group.revokeRuntimePermissions(false);
235                     }
236                     group.resetReviewRequired();
237                 }
238             }
239         }
240 
bindUi()241         private void bindUi() {
242             Activity activity = getActivity();
243             if (activity == null) {
244                 return;
245             }
246 
247             // Set icon
248             Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon(
249                     activity.getPackageManager());
250             ImageView iconView = (ImageView) activity.findViewById(R.id.app_icon);
251             iconView.setImageDrawable(icon);
252 
253             // Set message
254             String appLabel = mAppPermissions.getAppLabel().toString();
255             final int labelTemplateResId = isPackageUpdated()
256                     ?  R.string.permission_review_title_template_update
257                     :  R.string.permission_review_title_template_install;
258             SpannableString message = new SpannableString(getString(labelTemplateResId, appLabel));
259             // Set the permission message as the title so it can be announced.
260             activity.setTitle(message);
261 
262             // Color the app name.
263             final int appLabelStart = message.toString().indexOf(appLabel, 0);
264             final int appLabelLength = appLabel.length();
265 
266             TypedValue typedValue = new TypedValue();
267             activity.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
268             final int color = activity.getColor(typedValue.resourceId);
269 
270             message.setSpan(new ForegroundColorSpan(color), appLabelStart,
271                     appLabelStart + appLabelLength, 0);
272             TextView permissionsMessageView = (TextView) activity.findViewById(
273                     R.id.permissions_message);
274             permissionsMessageView.setText(message);
275 
276 
277             mContinueButton = (Button) getActivity().findViewById(R.id.continue_button);
278             mContinueButton.setOnClickListener(this);
279 
280             mCancelButton = (Button) getActivity().findViewById(R.id.cancel_button);
281             mCancelButton.setOnClickListener(this);
282         }
283 
loadPreferences()284         private void loadPreferences() {
285             Activity activity = getActivity();
286             if (activity == null) {
287                 return;
288             }
289 
290             PreferenceScreen screen = getPreferenceScreen();
291             if (screen == null) {
292                 screen = getPreferenceManager().createPreferenceScreen(getActivity());
293                 setPreferenceScreen(screen);
294             } else {
295                 screen.removeAll();
296             }
297 
298             PreferenceGroup currentPermissionsCategory = null;
299             PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory;
300             mNewPermissionsCategory = null;
301 
302             final boolean isPackageUpdated = isPackageUpdated();
303 
304             for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
305                 if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)
306                         || !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
307                     continue;
308                 }
309 
310                 // TODO: Sort permissions - platform first then third-party ones
311 
312                 final SwitchPreference preference;
313                 Preference cachedPreference = oldNewPermissionsCategory != null
314                         ? oldNewPermissionsCategory.findPreference(group.getName()) : null;
315                 if (cachedPreference instanceof SwitchPreference) {
316                     preference = (SwitchPreference) cachedPreference;
317                 } else {
318                     preference = new SwitchPreference(getActivity());
319 
320                     preference.setKey(group.getName());
321                     Drawable icon = Utils.loadDrawable(activity.getPackageManager(),
322                             group.getIconPkg(), group.getIconResId());
323                     preference.setIcon(Utils.applyTint(getContext(), icon,
324                             android.R.attr.colorControlNormal));
325                     preference.setTitle(group.getLabel());
326                     preference.setSummary(group.getDescription());
327                     preference.setPersistent(false);
328 
329                     preference.setOnPreferenceChangeListener(this);
330                 }
331 
332                 preference.setChecked(group.areRuntimePermissionsGranted());
333 
334                 // Mutable state
335                 if (group.isPolicyFixed()) {
336                     preference.setEnabled(false);
337                     preference.setSummary(getString(
338                             R.string.permission_summary_enforced_by_policy));
339                 } else {
340                     preference.setEnabled(true);
341                 }
342 
343                 if (group.isReviewRequired()) {
344                     if (!isPackageUpdated) {
345                         screen.addPreference(preference);
346                     } else {
347                         if (mNewPermissionsCategory == null) {
348                             mNewPermissionsCategory = new PreferenceCategory(activity);
349                             mNewPermissionsCategory.setTitle(R.string.new_permissions_category);
350                             mNewPermissionsCategory.setOrder(1);
351                             screen.addPreference(mNewPermissionsCategory);
352                         }
353                         mNewPermissionsCategory.addPreference(preference);
354                     }
355                 } else {
356                     if (currentPermissionsCategory == null) {
357                         currentPermissionsCategory = new PreferenceCategory(activity);
358                         currentPermissionsCategory.setTitle(R.string.current_permissions_category);
359                         currentPermissionsCategory.setOrder(2);
360                         screen.addPreference(currentPermissionsCategory);
361                     }
362                     currentPermissionsCategory.addPreference(preference);
363                 }
364             }
365         }
366 
isPackageUpdated()367         private boolean isPackageUpdated() {
368             List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups();
369             final int groupCount = groups.size();
370             for (int i = 0; i < groupCount; i++) {
371                 AppPermissionGroup group = groups.get(i);
372                 if (!group.isReviewRequired()) {
373                     return true;
374                 }
375             }
376             return false;
377         }
378 
executeCallback(boolean success)379         private void executeCallback(boolean success) {
380             Activity activity = getActivity();
381             if (activity == null) {
382                 return;
383             }
384             if (success) {
385                 IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
386                 if (intent != null) {
387                     try {
388                         int flagMask = 0;
389                         int flagValues = 0;
390                         if (activity.getIntent().getBooleanExtra(
391                                 Intent.EXTRA_RESULT_NEEDED, false)) {
392                             flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
393                             flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
394                         }
395                         activity.startIntentSenderForResult(intent, -1, null,
396                                 flagMask, flagValues, 0);
397                     } catch (IntentSender.SendIntentException e) {
398                         /* ignore */
399                     }
400                     return;
401                 }
402             }
403             RemoteCallback callback = activity.getIntent().getParcelableExtra(
404                     Intent.EXTRA_REMOTE_CALLBACK);
405             if (callback != null) {
406                 Bundle result = new Bundle();
407                 result.putBoolean(Intent.EXTRA_RETURN_RESULT, success);
408                 callback.sendResult(result);
409             }
410         }
411     }
412 }
413