1 /*
2  * Copyright (C) 2017 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.settings.applications.appinfo;
18 
19 import android.app.Activity;
20 import android.content.ActivityNotFoundException;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.icu.text.ListFormatter;
26 import android.icu.text.MessageFormat;
27 import android.util.Log;
28 
29 import androidx.annotation.VisibleForTesting;
30 import androidx.preference.Preference;
31 
32 import com.android.settings.R;
33 import com.android.settingslib.applications.PermissionsSummaryHelper;
34 import com.android.settingslib.core.lifecycle.LifecycleObserver;
35 import com.android.settingslib.core.lifecycle.events.OnStart;
36 import com.android.settingslib.core.lifecycle.events.OnStop;
37 
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.Random;
44 
45 /**
46  * A PreferenceController handling the logic for permissions of apps.
47  */
48 public class AppPermissionPreferenceController extends AppInfoPreferenceControllerBase implements
49         LifecycleObserver, OnStart, OnStop {
50 
51     private static final String TAG = "PermissionPrefControl";
52     private static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
53     private static final long INVALID_SESSION_ID = 0;
54 
55     private final PackageManager mPackageManager;
56 
57     private String mPackageName;
58 
59     @VisibleForTesting
60     final PermissionsSummaryHelper.PermissionsResultCallback mPermissionCallback
61             = new PermissionsSummaryHelper.PermissionsResultCallback() {
62         @Override
63         public void onPermissionSummaryResult(
64                 int requestedPermissionCount, int additionalGrantedPermissionCount,
65                 List<CharSequence> grantedGroupLabels) {
66             final Resources res = mContext.getResources();
67             CharSequence summary;
68 
69             if (requestedPermissionCount == 0) {
70                 summary = res.getString(
71                         R.string.runtime_permissions_summary_no_permissions_requested);
72                 mPreference.setEnabled(false);
73             } else {
74                 final ArrayList<CharSequence> list = new ArrayList<>(grantedGroupLabels);
75                 if (additionalGrantedPermissionCount > 0) {
76                     // N additional permissions.
77                     MessageFormat msgFormat = new MessageFormat(
78                             res.getString(R.string.runtime_permissions_additional_count),
79                             Locale.getDefault());
80                     Map<String, Object> arguments = new HashMap<>();
81                     arguments.put("count", additionalGrantedPermissionCount);
82                     list.add(msgFormat.format(arguments));
83                 }
84                 if (list.size() == 0) {
85                     summary = res.getString(
86                             R.string.runtime_permissions_summary_no_permissions_granted);
87                 } else {
88                     summary = ListFormatter.getInstance().format(list);
89                 }
90                 mPreference.setEnabled(true);
91             }
92             mPreference.setSummary(summary);
93         }
94     };
95 
96     private final PackageManager.OnPermissionsChangedListener mOnPermissionsChangedListener =
97             uid -> updateState(mPreference);
98 
AppPermissionPreferenceController(Context context, String key)99     public AppPermissionPreferenceController(Context context, String key) {
100         super(context, key);
101         mPackageManager = context.getPackageManager();
102     }
103 
104     @Override
onStart()105     public void onStart() {
106         mPackageManager.addOnPermissionsChangeListener(mOnPermissionsChangedListener);
107     }
108 
109     @Override
onStop()110     public void onStop() {
111         mPackageManager.removeOnPermissionsChangeListener(mOnPermissionsChangedListener);
112     }
113 
114     @Override
updateState(Preference preference)115     public void updateState(Preference preference) {
116         PermissionsSummaryHelper.getPermissionSummary(mContext, mPackageName, mPermissionCallback);
117     }
118 
119     @Override
handlePreferenceTreeClick(Preference preference)120     public boolean handlePreferenceTreeClick(Preference preference) {
121         if (getPreferenceKey().equals(preference.getKey())) {
122             startManagePermissionsActivity();
123             return true;
124         }
125         return false;
126     }
127 
setPackageName(String packageName)128     public void setPackageName(String packageName) {
129         mPackageName = packageName;
130     }
131 
startManagePermissionsActivity()132     private void startManagePermissionsActivity() {
133         // start new activity to manage app permissions
134         final Intent permIntent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
135         permIntent.setPackage(mPackageManager.getPermissionControllerPackageName());
136         permIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, mParent.getAppEntry().info.packageName);
137         permIntent.putExtra(EXTRA_HIDE_INFO_BUTTON, true);
138         Activity activity = mParent.getActivity();
139         Intent intent = activity != null ? activity.getIntent() : null;
140         if (intent != null) {
141             String action = intent.getAction();
142             long sessionId = intent.getLongExtra(
143                     Intent.ACTION_AUTO_REVOKE_PERMISSIONS, INVALID_SESSION_ID);
144             if ((action != null && action.equals(Intent.ACTION_AUTO_REVOKE_PERMISSIONS))
145                     || sessionId != INVALID_SESSION_ID) {
146                 // If intent is Auto revoke, and we don't already have a session ID, make one
147                 while (sessionId == INVALID_SESSION_ID) {
148                     sessionId = new Random().nextLong();
149                 }
150                 permIntent.putExtra(Intent.ACTION_AUTO_REVOKE_PERMISSIONS, sessionId);
151             }
152         }
153         try {
154             if (activity != null) {
155                 activity.startActivityForResult(permIntent, mParent.SUB_INFO_FRAGMENT);
156             }
157         } catch (ActivityNotFoundException e) {
158             Log.w(TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS");
159         }
160     }
161 }
162