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