• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 /*
18  * This file references fs_error.png, fs_good.png, fs_indeterminate.png,
19  * and fs_warning.png which are licensed under Creative Commons 3.0
20  * by fatcow.com.
21  * http://www.fatcow.com/free-icons/
22  * http://creativecommons.org/licenses/by/3.0/us/
23  */
24 
25 package com.android.cts.verifier.features;
26 
27 import com.android.cts.verifier.PassFailButtons;
28 import com.android.cts.verifier.R;
29 
30 import android.content.pm.FeatureInfo;
31 import android.content.pm.PackageManager;
32 import android.os.Build;
33 import android.os.Bundle;
34 import android.view.View;
35 import android.widget.ImageView;
36 import android.widget.SimpleAdapter;
37 import android.widget.TextView;
38 
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.Comparator;
42 import java.util.HashMap;
43 import java.util.LinkedHashSet;
44 import java.util.Set;
45 
46 public class FeatureSummaryActivity extends PassFailButtons.ListActivity {
47     /**
48      * Simple storage class for data about an Android feature.
49      */
50     static class Feature {
51         /**
52          * The name of the feature. Should be one of the PackageManager.FEATURE*
53          * constants.
54          */
55         public String name;
56 
57         /**
58          * Indicates whether the field is present on the current device.
59          */
60         public boolean present;
61 
62         /**
63          * Indicates whether the field is required for the current device.
64          */
65         public boolean required;
66 
67         /**
68          * Constructor does not include 'present' because that's a detected
69          * value, and not set during creation.
70          *
71          * @param name value for this.name
72          * @param required value for this.required
73          */
Feature(String name, boolean required)74         public Feature(String name, boolean required) {
75             this.name = name;
76             this.required = required;
77             this.present = false;
78         }
79 
80         @Override
equals(Object o)81         public boolean equals(Object o) {
82             if (this == o) {
83                 return true;
84             } else if (o == null || !(o instanceof Feature)) {
85                 return false;
86             } else {
87                 Feature feature = (Feature) o;
88                 return name.equals(feature.name);
89             }
90         }
91 
92         @Override
hashCode()93         public int hashCode() {
94             return name.hashCode();
95         }
96     }
97 
98     public static final Feature[] ALL_ECLAIR_FEATURES = {
99             new Feature(PackageManager.FEATURE_CAMERA, true),
100             new Feature(PackageManager.FEATURE_CAMERA_AUTOFOCUS, false),
101             new Feature(PackageManager.FEATURE_CAMERA_FLASH, false),
102             new Feature(PackageManager.FEATURE_LIVE_WALLPAPER, false),
103             new Feature(PackageManager.FEATURE_SENSOR_LIGHT, false),
104             new Feature(PackageManager.FEATURE_SENSOR_PROXIMITY, false),
105             new Feature(PackageManager.FEATURE_TELEPHONY, false),
106             new Feature(PackageManager.FEATURE_TELEPHONY_CDMA, false),
107             new Feature(PackageManager.FEATURE_TELEPHONY_GSM, false),
108     };
109 
110     public static final Feature[] ALL_FROYO_FEATURES = {
111             new Feature("android.hardware.bluetooth", true),
112             new Feature("android.hardware.location", true),
113             new Feature("android.hardware.location.gps", true),
114             new Feature("android.hardware.location.network", true),
115             new Feature("android.hardware.microphone", true),
116             new Feature("android.hardware.sensor.accelerometer", true),
117             new Feature("android.hardware.sensor.compass", true),
118             new Feature("android.hardware.touchscreen", true),
119             new Feature("android.hardware.touchscreen.multitouch", false),
120             new Feature("android.hardware.touchscreen.multitouch.distinct", false),
121             new Feature("android.hardware.wifi", false),
122     };
123 
124     public static final Feature[] ALL_GINGERBREAD_FEATURES = {
125             // Required features in prior releases that became optional in GB
126             new Feature("android.hardware.bluetooth", false),
127             new Feature("android.hardware.camera", false),
128             new Feature("android.hardware.location.gps", false),
129             new Feature("android.hardware.microphone", false),
130             new Feature("android.hardware.sensor.accelerometer", false),
131             new Feature("android.hardware.sensor.compass", false),
132 
133             // New features in GB
134             new Feature("android.hardware.audio.low_latency", false),
135             new Feature("android.hardware.camera.front", false),
136             new Feature("android.hardware.nfc", false),
137             new Feature("android.hardware.sensor.barometer", false),
138             new Feature("android.hardware.sensor.gyroscope", false),
139             new Feature("android.hardware.touchscreen.multitouch.jazzhand", false),
140             new Feature("android.software.sip", false),
141             new Feature("android.software.sip.voip", false),
142     };
143 
144     public static final Feature[] ALL_GINGERBREAD_MR1_FEATURES = {
145             new Feature("android.hardware.usb.accessory", false),
146     };
147 
148     public static final Feature[] ALL_HONEYCOMB_FEATURES = {
149             // Required features in prior releases that became optional in HC
150             new Feature("android.hardware.touchscreen", false),
151 
152             new Feature("android.hardware.faketouch", true),
153     };
154 
155     public static final Feature[] ALL_HONEYCOMB_MR1_FEATURES = {
156             new Feature("android.hardware.usb.host", false),
157             new Feature("android.hardware.usb.accessory", false),
158     };
159 
160     public static final Feature[] ALL_HONEYCOMB_MR2_FEATURES = {
161             new Feature("android.hardware.faketouch.multitouch.distinct", false),
162             new Feature("android.hardware.faketouch.multitouch.jazzhand", false),
163             new Feature("android.hardware.screen.landscape", false),
164             new Feature("android.hardware.screen.portrait", false),
165     };
166 
167     public static final Feature[] ALL_ICE_CREAM_SANDWICH_FEATURES = {
168             new Feature(PackageManager.FEATURE_WIFI_DIRECT, false),
169     };
170 
171     public static final Feature[] ALL_JELLY_BEAN_FEATURES = {
172             // Required features in prior releases that became optional
173             new Feature(PackageManager.FEATURE_FAKETOUCH, false),
174 
175             //new feature in JB
176             new Feature(PackageManager.FEATURE_TELEVISION, false),
177     };
178 
179     public static final Feature[] ALL_JELLY_BEAN_MR2_FEATURES = {
180             new Feature("android.software.app_widgets", false),
181             new Feature("android.software.input_methods", false),
182             new Feature("android.software.home_screen", false),
183             new Feature("android.hardware.bluetooth_le", false),
184             new Feature("android.hardware.camera.any", false),
185     };
186 
187     public static final Feature[] ALL_KITKAT_FEATURES = {
188             new Feature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, false),
189             new Feature(PackageManager.FEATURE_CONSUMER_IR, false),
190             new Feature(PackageManager.FEATURE_DEVICE_ADMIN, false),
191             new Feature(PackageManager.FEATURE_SENSOR_STEP_COUNTER, false),
192             new Feature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR, false),
193     };
194 
195     public static final Feature[] ALL_KITKAT_WATCH_FEATURES = {
196             new Feature(PackageManager.FEATURE_SENSOR_HEART_RATE, false),
197             new Feature(PackageManager.FEATURE_BACKUP, false),
198             new Feature(PackageManager.FEATURE_PRINTING, false),
199             new Feature(PackageManager.FEATURE_WATCH, false),
200             new Feature(PackageManager.FEATURE_WEBVIEW, false),
201             new Feature(PackageManager.FEATURE_CAMERA_EXTERNAL, false),
202     };
203 
204     public static final Feature[] ALL_LOLLIPOP_FEATURES = {
205             // New features in L
206             new Feature(PackageManager.FEATURE_AUDIO_OUTPUT, false),
207             new Feature(PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING, false),
208             new Feature(PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR, false),
209             new Feature(PackageManager.FEATURE_CAMERA_CAPABILITY_RAW, false),
210             new Feature(PackageManager.FEATURE_CAMERA_LEVEL_FULL, false),
211             new Feature(PackageManager.FEATURE_CONNECTION_SERVICE, false),
212             new Feature(PackageManager.FEATURE_GAMEPAD, false),
213             new Feature(PackageManager.FEATURE_LEANBACK, false),
214             new Feature(PackageManager.FEATURE_LIVE_TV, false),
215             new Feature(PackageManager.FEATURE_MANAGED_USERS, false),
216             new Feature(PackageManager.FEATURE_OPENGLES_EXTENSION_PACK, false),
217             new Feature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, false),
218             new Feature(PackageManager.FEATURE_SENSOR_AMBIENT_TEMPERATURE, false),
219             new Feature(PackageManager.FEATURE_SENSOR_HEART_RATE_ECG, false),
220             new Feature(PackageManager.FEATURE_SENSOR_RELATIVE_HUMIDITY, false),
221             new Feature(PackageManager.FEATURE_VERIFIED_BOOT, false),
222 
223             // New hidden features in L
224             new Feature("android.hardware.ethernet", false),
225             new Feature("android.hardware.hdmi.cec", false),
226             new Feature("android.software.leanback_only", false),
227             new Feature("android.software.voice_recognizers", false),
228     };
229 
230     @Override
onCreate(Bundle savedInstanceState)231     public void onCreate(Bundle savedInstanceState) {
232         super.onCreate(savedInstanceState);
233         setContentView(R.layout.fs_main);
234         setPassFailButtonClickListeners();
235         setInfoResources(R.string.feature_summary, R.string.feature_summary_info, R.layout.fs_info);
236 
237         // some values used to detect warn-able conditions involving multiple
238         // features
239         boolean hasWifi = false;
240         boolean hasTelephony = false;
241         boolean hasIllegalFeature = false;
242 
243         // get list of all features device thinks it has, & store in a HashMap
244         // for fast lookups
245         HashMap<String, String> actualFeatures = new HashMap<String, String>();
246         for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
247             actualFeatures.put(fi.name, fi.name);
248         }
249 
250         // data structure that the SimpleAdapter will use to populate ListView
251         ArrayList<HashMap<String, Object>> listViewData = new ArrayList<HashMap<String, Object>>();
252 
253         // roll over all known features & check whether device reports them
254         boolean present = false;
255         int statusIcon;
256         Set<Feature> features = new LinkedHashSet<Feature>();
257 
258         // add features from latest to last so that the latest requirements are put in the set first
259         int apiVersion = Build.VERSION.SDK_INT;
260         if (apiVersion >= Build.VERSION_CODES.LOLLIPOP) {
261             Collections.addAll(features, ALL_LOLLIPOP_FEATURES);
262         }
263         if (apiVersion >= Build.VERSION_CODES.KITKAT_WATCH) {
264             Collections.addAll(features, ALL_KITKAT_WATCH_FEATURES);
265         }
266         if (apiVersion >= Build.VERSION_CODES.KITKAT) {
267             Collections.addAll(features, ALL_KITKAT_FEATURES);
268         }
269         if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
270             Collections.addAll(features, ALL_JELLY_BEAN_MR2_FEATURES);
271         }
272         if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN) {
273             Collections.addAll(features, ALL_JELLY_BEAN_FEATURES);
274         }
275         if (apiVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
276             Collections.addAll(features, ALL_ICE_CREAM_SANDWICH_FEATURES);
277         }
278         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR2) {
279             Collections.addAll(features, ALL_HONEYCOMB_MR2_FEATURES);
280         }
281         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR1) {
282             Collections.addAll(features, ALL_HONEYCOMB_MR1_FEATURES);
283         }
284         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB) {
285             Collections.addAll(features, ALL_HONEYCOMB_FEATURES);
286         }
287         if (apiVersion >= Build.VERSION_CODES.GINGERBREAD_MR1) {
288             Collections.addAll(features, ALL_GINGERBREAD_MR1_FEATURES);
289         }
290         if (apiVersion >= Build.VERSION_CODES.GINGERBREAD) {
291             Collections.addAll(features, ALL_GINGERBREAD_FEATURES);
292         }
293         if (apiVersion >= Build.VERSION_CODES.FROYO) {
294             Collections.addAll(features, ALL_FROYO_FEATURES);
295         }
296         if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
297             Collections.addAll(features, ALL_ECLAIR_FEATURES);
298         }
299         for (Feature f : features) {
300             HashMap<String, Object> row = new HashMap<String, Object>();
301             listViewData.add(row);
302             present = actualFeatures.containsKey(f.name);
303             if (present) {
304                 // device reports it -- yay! set the happy icon
305                 hasWifi = hasWifi || PackageManager.FEATURE_WIFI.equals(f.name);
306                 hasTelephony = hasTelephony || PackageManager.FEATURE_TELEPHONY.equals(f.name);
307                 statusIcon = R.drawable.fs_good;
308                 actualFeatures.remove(f.name);
309             } else if (!present && f.required) {
310                 // it's required, but device doesn't report it. Boo, set the
311                 // bogus icon
312                 statusIcon = R.drawable.fs_error;
313             } else {
314                 // device doesn't report it, but it's not req'd, so can't tell
315                 // if there's a problem
316                 statusIcon = R.drawable.fs_indeterminate;
317             }
318             row.put("feature", f.name);
319             row.put("icon", statusIcon);
320         }
321 
322         // now roll over any remaining features (which are non-standard)
323         for (String feature : actualFeatures.keySet()) {
324             if (feature == null || "".equals(feature))
325                 continue;
326             HashMap<String, Object> row = new HashMap<String, Object>();
327             listViewData.add(row);
328             row.put("feature", feature);
329             if (feature.startsWith("android")) { // intentionally not "android."
330                 // sorry, you're not allowed to squat in the official namespace;
331                 // set bogus icon
332                 row.put("icon", R.drawable.fs_error);
333                 hasIllegalFeature = true;
334             } else {
335                 // non-standard features are okay, but flag them just in case
336                 row.put("icon", R.drawable.fs_warning);
337             }
338         }
339 
340         // sort the ListView's data to group by icon type, for easier reading by
341         // humans
342         final HashMap<Integer, Integer> idMap = new HashMap<Integer, Integer>();
343         idMap.put(R.drawable.fs_error, 0);
344         idMap.put(R.drawable.fs_warning, 1);
345         idMap.put(R.drawable.fs_indeterminate, 2);
346         idMap.put(R.drawable.fs_good, 3);
347         Collections.sort(listViewData, new Comparator<HashMap<String, Object>>() {
348             public int compare(HashMap<String, Object> left, HashMap<String, Object> right) {
349                 int leftId = idMap.get(left.get("icon"));
350                 int rightId = idMap.get(right.get("icon"));
351                 if (leftId == rightId) {
352                     return ((String) left.get("feature")).compareTo((String) right.get("feature"));
353                 }
354                 if (leftId < rightId)
355                     return -1;
356                 return 1;
357             }
358         });
359 
360         // Set up the SimpleAdapter used to populate the ListView
361         SimpleAdapter adapter = new SimpleAdapter(this, listViewData, R.layout.fs_row,
362                 new String[] {
363                         "feature", "icon"
364                 }, new int[] {
365                         R.id.fs_feature, R.id.fs_icon
366                 });
367         adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
368             public boolean setViewValue(View view, Object data, String repr) {
369                 try {
370                     if (view instanceof ImageView) {
371                         ((ImageView) view).setImageResource((Integer) data);
372                     } else if (view instanceof TextView) {
373                         ((TextView) view).setText((String) data);
374                     } else {
375                         return false;
376                     }
377                     return true;
378                 } catch (ClassCastException e) {
379                     return false;
380                 }
381             }
382         });
383         setListAdapter(adapter);
384 
385         // finally, check for our second-order error cases and set warning text
386         // if necessary
387         StringBuffer sb = new StringBuffer();
388         if (hasIllegalFeature) {
389             sb.append(getResources().getString(R.string.fs_disallowed)).append("\n");
390         }
391         if (!hasWifi && !hasTelephony) {
392             sb.append(getResources().getString(R.string.fs_missing_wifi_telephony)).append("\n");
393         }
394         String warnings = sb.toString().trim();
395         if (warnings == null || "".equals(warnings)) {
396             ((TextView) (findViewById(R.id.fs_warnings))).setVisibility(View.GONE);
397         } else {
398             ((TextView) (findViewById(R.id.fs_warnings))).setText(warnings);
399         }
400     }
401 }
402