• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.security;
18 
19 import android.content.Context;
20 import android.content.IContentProvider;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.content.res.Resources;
24 import android.graphics.drawable.Drawable;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.support.annotation.VisibleForTesting;
28 import android.support.v7.preference.Preference;
29 import android.support.v7.preference.PreferenceScreen;
30 import android.text.TextUtils;
31 import android.util.ArrayMap;
32 import android.util.Pair;
33 
34 import com.android.settings.R;
35 import com.android.settings.trustagent.TrustAgentManager;
36 import com.android.settings.trustagent.TrustAgentManagerImpl;
37 import com.android.settingslib.drawer.DashboardCategory;
38 import com.android.settingslib.drawer.Tile;
39 import com.android.settingslib.drawer.TileUtils;
40 
41 import java.util.Map;
42 import java.util.TreeMap;
43 import java.util.concurrent.Executors;
44 
45 /** Implementation for {@code SecurityFeatureProvider}. */
46 public class SecurityFeatureProviderImpl implements SecurityFeatureProvider {
47 
48     private TrustAgentManager mTrustAgentManager;
49 
50     @VisibleForTesting
51     static final Drawable DEFAULT_ICON = null;
52 
53     @VisibleForTesting
54     static Map<String, Pair<String, Integer>> sIconCache = new TreeMap<>();
55 
56     @VisibleForTesting
57     static Map<String, String> sSummaryCache = new TreeMap<>();
58 
59     /** Update preferences with data from associated tiles. */
updatePreferences(final Context context, final PreferenceScreen preferenceScreen, final DashboardCategory dashboardCategory)60     public void updatePreferences(final Context context, final PreferenceScreen preferenceScreen,
61             final DashboardCategory dashboardCategory) {
62         if (preferenceScreen == null) {
63             return;
64         }
65         int tilesCount = (dashboardCategory != null) ? dashboardCategory.getTilesCount() : 0;
66         if (tilesCount == 0) {
67             return;
68         }
69 
70         initPreferences(context, preferenceScreen, dashboardCategory);
71 
72         // Fetching the summary and icon from the provider introduces latency, so do this on a
73         // separate thread.
74         Executors.newSingleThreadExecutor().execute(new Runnable() {
75             @Override
76             public void run() {
77                 updatePreferencesToRunOnWorkerThread(context, preferenceScreen, dashboardCategory);
78             }
79         });
80     }
81 
82     @VisibleForTesting
initPreferences(Context context, PreferenceScreen preferenceScreen, DashboardCategory dashboardCategory)83     static void initPreferences(Context context, PreferenceScreen preferenceScreen,
84             DashboardCategory dashboardCategory) {
85         int tilesCount = (dashboardCategory != null) ? dashboardCategory.getTilesCount() : 0;
86         for (int i = 0; i < tilesCount; i++) {
87             Tile tile = dashboardCategory.getTile(i);
88             // If the tile does not have a key or appropriate meta data, skip it.
89             if (TextUtils.isEmpty(tile.key) || (tile.metaData == null)) {
90                 continue;
91             }
92             Preference matchingPref = preferenceScreen.findPreference(tile.key);
93             // If the tile does not have a matching preference, skip it.
94             if (matchingPref == null) {
95                 continue;
96             }
97             // Either remove an icon by replacing them with nothing, or use the cached one since
98             // there is a delay in fetching the injected icon, and we don't want an inappropriate
99             // icon to be displayed while waiting for the injected icon.
100             final String iconUri =
101                     tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null);
102             Drawable drawable = DEFAULT_ICON;
103             if ((iconUri != null) && sIconCache.containsKey(iconUri)) {
104                 Pair<String, Integer> icon = sIconCache.get(iconUri);
105                 try {
106                     drawable = context.getPackageManager()
107                             .getResourcesForApplication(icon.first /* package name */)
108                                     .getDrawable(icon.second /* res id */,
109                                             context.getTheme());
110                 } catch (PackageManager.NameNotFoundException e) {
111                     // Ignore and just load the default icon.
112                 }
113             }
114             matchingPref.setIcon(drawable);
115             // Either reserve room for the summary or load the cached one. This prevents the title
116             // from shifting when the final summary is injected.
117             final String summaryUri =
118                     tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, null);
119             String summary = context.getString(R.string.summary_placeholder);
120             if ((summaryUri != null) && sSummaryCache.containsKey(summaryUri)) {
121                 summary = sSummaryCache.get(summaryUri);
122             }
123             matchingPref.setSummary(summary);
124         }
125     }
126 
127     @VisibleForTesting
updatePreferencesToRunOnWorkerThread(Context context, PreferenceScreen preferenceScreen, DashboardCategory dashboardCategory)128     void updatePreferencesToRunOnWorkerThread(Context context, PreferenceScreen preferenceScreen,
129             DashboardCategory dashboardCategory) {
130 
131         int tilesCount = (dashboardCategory != null) ? dashboardCategory.getTilesCount() : 0;
132         Map<String, IContentProvider> providerMap = new ArrayMap<>();
133         for (int i = 0; i < tilesCount; i++) {
134             Tile tile = dashboardCategory.getTile(i);
135             // If the tile does not have a key or appropriate meta data, skip it.
136             if (TextUtils.isEmpty(tile.key) || (tile.metaData == null)) {
137                 continue;
138             }
139             Preference matchingPref = preferenceScreen.findPreference(tile.key);
140             // If the tile does not have a matching preference, skip it.
141             if (matchingPref == null) {
142                 continue;
143             }
144             // Check if the tile has content providers for dynamically updatable content.
145             final String iconUri =
146                     tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null);
147             final String summaryUri =
148                     tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, null);
149             if (!TextUtils.isEmpty(iconUri)) {
150                 String packageName = null;
151                 if (tile.intent != null) {
152                     Intent intent = tile.intent;
153                     if (!TextUtils.isEmpty(intent.getPackage())) {
154                         packageName = intent.getPackage();
155                     } else if (intent.getComponent() != null) {
156                         packageName = intent.getComponent().getPackageName();
157                     }
158                 }
159                 Pair<String, Integer> icon =
160                         TileUtils.getIconFromUri(context, packageName, iconUri, providerMap);
161                 if (icon != null) {
162                     sIconCache.put(iconUri, icon);
163                     // Icon is only returned if the icon belongs to Settings or the target app.
164                     // setIcon must be called on the UI thread.
165                     new Handler(Looper.getMainLooper()).post(new Runnable() {
166                         @Override
167                         public void run() {
168                             try {
169                                 matchingPref.setIcon(context.getPackageManager()
170                                         .getResourcesForApplication(icon.first /* package name */)
171                                                 .getDrawable(icon.second /* res id */,
172                                                         context.getTheme()));
173                             } catch (PackageManager.NameNotFoundException
174                                     | Resources.NotFoundException e) {
175                                 // Intentionally ignored. If icon resources cannot be found, do not
176                                 // update.
177                             }
178                         }
179                     });
180                 }
181             }
182             if (!TextUtils.isEmpty(summaryUri)) {
183                 String summary = TileUtils.getTextFromUri(context, summaryUri, providerMap,
184                         TileUtils.META_DATA_PREFERENCE_SUMMARY);
185                 sSummaryCache.put(summaryUri, summary);
186                 // setSummary must be called on UI thread.
187                 new Handler(Looper.getMainLooper()).post(new Runnable() {
188                     @Override
189                     public void run() {
190                         // Only update the summary if it has actually changed.
191                         if (summary == null) {
192                             if (matchingPref.getSummary() != null) {
193                                 matchingPref.setSummary(summary);
194                             }
195                         } else if (!summary.equals(matchingPref.getSummary())) {
196                             matchingPref.setSummary(summary);
197                         }
198                     }
199                 });
200             }
201         }
202     }
203 
204     @Override
getTrustAgentManager()205     public TrustAgentManager getTrustAgentManager() {
206         if (mTrustAgentManager == null) {
207             mTrustAgentManager = new TrustAgentManagerImpl();
208         }
209         return mTrustAgentManager;
210     }
211 }
212