• 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             // Features explicitly made optional in L
224             new Feature(PackageManager.FEATURE_LOCATION_NETWORK, false),
225 
226             // New hidden features in L
227             new Feature("android.hardware.ethernet", false),
228             new Feature("android.hardware.hdmi.cec", false),
229             new Feature("android.software.leanback_only", false),
230             new Feature("android.software.voice_recognizers", false),
231     };
232 
233     public static final Feature[] ALL_MNC_FEATURES = {
234             new Feature(PackageManager.FEATURE_MIDI, false),
235             new Feature(PackageManager.FEATURE_AUDIO_PRO, false),
236             new Feature(PackageManager.FEATURE_AUTOMOTIVE, false),
237             new Feature(PackageManager.FEATURE_HIFI_SENSORS, false),
238             new Feature(PackageManager.FEATURE_FINGERPRINT, false),
239     };
240 
241     public static final Feature[] ALL_NYC_FEATURES = {
242             new Feature(PackageManager.FEATURE_VR_MODE, false),
243             new Feature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE, false),
244             new Feature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, false),
245             new Feature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, false),
246             new Feature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, false),
247             new Feature(PackageManager.FEATURE_PICTURE_IN_PICTURE, false),
248             new Feature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, false),
249             // FEATURE_FILE_BASED_ENCRYPTION is hide
250             new Feature("android.software.file_based_encryption", false),
251     };
252 
253     public static final Feature[] ALL_O_FEATURES = {
254             new Feature(PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE, false),
255             // FEATURE_TELEPHONY_CARRIERLOCK is SystemApi
256             new Feature("android.hardware.telephony.carrierlock", false),
257             new Feature(PackageManager.FEATURE_WIFI_AWARE, false),
258             new Feature(PackageManager.FEATURE_EMBEDDED, false),
259             new Feature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP, false),
260             new Feature(PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS, false),
261             new Feature(PackageManager.FEATURE_VR_HEADTRACKING, false),
262             // FEATURE_CTS is hide
263             new Feature("android.software.cts", false),
264             new Feature(PackageManager.FEATURE_WIFI_AWARE, false),
265     };
266 
267     @Override
onCreate(Bundle savedInstanceState)268     public void onCreate(Bundle savedInstanceState) {
269         super.onCreate(savedInstanceState);
270         setContentView(R.layout.fs_main);
271         setPassFailButtonClickListeners();
272         setInfoResources(R.string.feature_summary, R.string.feature_summary_info, R.layout.fs_info);
273 
274         // some values used to detect warn-able conditions involving multiple
275         // features
276         boolean hasWifi = false;
277         boolean hasTelephony = false;
278         boolean hasBluetooth = false;
279         boolean hasIllegalFeature = false;
280         boolean hasTelevision = false;
281 
282         // get list of all features device thinks it has, & store in a HashMap
283         // for fast lookups
284         HashMap<String, String> actualFeatures = new HashMap<String, String>();
285         for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
286             actualFeatures.put(fi.name, fi.name);
287         }
288 
289         // data structure that the SimpleAdapter will use to populate ListView
290         ArrayList<HashMap<String, Object>> listViewData = new ArrayList<HashMap<String, Object>>();
291 
292         // roll over all known features & check whether device reports them
293         boolean present = false;
294         int statusIcon;
295         Set<Feature> features = new LinkedHashSet<Feature>();
296 
297         // add features from latest to last so that the latest requirements are put in the set first
298         int apiVersion = Build.VERSION.SDK_INT;
299         if (apiVersion >= Build.VERSION_CODES.O) {
300             Collections.addAll(features, ALL_O_FEATURES);
301         }
302         if (apiVersion >= Build.VERSION_CODES.N) {
303             Collections.addAll(features, ALL_NYC_FEATURES);
304         }
305         if (apiVersion >= Build.VERSION_CODES.M) {
306             Collections.addAll(features, ALL_MNC_FEATURES);
307         }
308         if (apiVersion >= Build.VERSION_CODES.LOLLIPOP) {
309             Collections.addAll(features, ALL_LOLLIPOP_FEATURES);
310         }
311         if (apiVersion >= Build.VERSION_CODES.KITKAT_WATCH) {
312             Collections.addAll(features, ALL_KITKAT_WATCH_FEATURES);
313         }
314         if (apiVersion >= Build.VERSION_CODES.KITKAT) {
315             Collections.addAll(features, ALL_KITKAT_FEATURES);
316         }
317         if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
318             Collections.addAll(features, ALL_JELLY_BEAN_MR2_FEATURES);
319         }
320         if (apiVersion >= Build.VERSION_CODES.JELLY_BEAN) {
321             Collections.addAll(features, ALL_JELLY_BEAN_FEATURES);
322         }
323         if (apiVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
324             Collections.addAll(features, ALL_ICE_CREAM_SANDWICH_FEATURES);
325         }
326         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR2) {
327             Collections.addAll(features, ALL_HONEYCOMB_MR2_FEATURES);
328         }
329         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB_MR1) {
330             Collections.addAll(features, ALL_HONEYCOMB_MR1_FEATURES);
331         }
332         if (apiVersion >= Build.VERSION_CODES.HONEYCOMB) {
333             Collections.addAll(features, ALL_HONEYCOMB_FEATURES);
334         }
335         if (apiVersion >= Build.VERSION_CODES.GINGERBREAD_MR1) {
336             Collections.addAll(features, ALL_GINGERBREAD_MR1_FEATURES);
337         }
338         if (apiVersion >= Build.VERSION_CODES.GINGERBREAD) {
339             Collections.addAll(features, ALL_GINGERBREAD_FEATURES);
340         }
341         if (apiVersion >= Build.VERSION_CODES.FROYO) {
342             Collections.addAll(features, ALL_FROYO_FEATURES);
343         }
344         if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
345             Collections.addAll(features, ALL_ECLAIR_FEATURES);
346         }
347 
348         hasTelevision = getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
349         for (Feature f : features) {
350             HashMap<String, Object> row = new HashMap<String, Object>();
351             listViewData.add(row);
352             present = actualFeatures.containsKey(f.name);
353             if (present) {
354                 // device reports it -- yay! set the happy icon
355                 hasWifi = hasWifi || PackageManager.FEATURE_WIFI.equals(f.name);
356                 hasTelephony = hasTelephony || PackageManager.FEATURE_TELEPHONY.equals(f.name);
357                 hasBluetooth = hasBluetooth || PackageManager.FEATURE_BLUETOOTH.equals(f.name);
358                 statusIcon = R.drawable.fs_good;
359                 actualFeatures.remove(f.name);
360             } else if (!present && f.required) {
361                 // it's required, but device doesn't report it. Boo, set the
362                 // bogus icon
363                 statusIcon = R.drawable.fs_error;
364                 if (hasTelevision && PackageManager.FEATURE_LOCATION.equals(f.name)) {
365                     statusIcon = R.drawable.fs_indeterminate;
366                 }
367             } else {
368                 // device doesn't report it, but it's not req'd, so can't tell
369                 // if there's a problem
370                 statusIcon = R.drawable.fs_indeterminate;
371             }
372             row.put("feature", f.name);
373             row.put("icon", statusIcon);
374         }
375 
376         // now roll over any remaining features (which are non-standard)
377         for (String feature : actualFeatures.keySet()) {
378             if (feature == null || "".equals(feature))
379                 continue;
380             HashMap<String, Object> row = new HashMap<String, Object>();
381             listViewData.add(row);
382             row.put("feature", feature);
383             if (feature.startsWith("android")) { // intentionally not "android."
384                 // sorry, you're not allowed to squat in the official namespace;
385                 // set bogus icon
386                 row.put("icon", R.drawable.fs_error);
387                 hasIllegalFeature = true;
388             } else {
389                 // non-standard features are okay, but flag them just in case
390                 row.put("icon", R.drawable.fs_warning);
391             }
392         }
393 
394         // sort the ListView's data to group by icon type, for easier reading by
395         // humans
396         final HashMap<Integer, Integer> idMap = new HashMap<Integer, Integer>();
397         idMap.put(R.drawable.fs_error, 0);
398         idMap.put(R.drawable.fs_warning, 1);
399         idMap.put(R.drawable.fs_indeterminate, 2);
400         idMap.put(R.drawable.fs_good, 3);
401         Collections.sort(listViewData, new Comparator<HashMap<String, Object>>() {
402             public int compare(HashMap<String, Object> left, HashMap<String, Object> right) {
403                 int leftId = idMap.get(left.get("icon"));
404                 int rightId = idMap.get(right.get("icon"));
405                 if (leftId == rightId) {
406                     return ((String) left.get("feature")).compareTo((String) right.get("feature"));
407                 }
408                 if (leftId < rightId)
409                     return -1;
410                 return 1;
411             }
412         });
413 
414         // Set up the SimpleAdapter used to populate the ListView
415         SimpleAdapter adapter = new SimpleAdapter(this, listViewData, R.layout.fs_row,
416                 new String[] {
417                         "feature", "icon"
418                 }, new int[] {
419                         R.id.fs_feature, R.id.fs_icon
420                 });
421         adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
422             public boolean setViewValue(View view, Object data, String repr) {
423                 try {
424                     if (view instanceof ImageView) {
425                         ((ImageView) view).setImageResource((Integer) data);
426                     } else if (view instanceof TextView) {
427                         ((TextView) view).setText((String) data);
428                     } else {
429                         return false;
430                     }
431                     return true;
432                 } catch (ClassCastException e) {
433                     return false;
434                 }
435             }
436         });
437         setListAdapter(adapter);
438 
439         // finally, check for our second-order error cases and set warning text
440         // if necessary
441         StringBuffer sb = new StringBuffer();
442         if (hasIllegalFeature) {
443             sb.append(getResources().getString(R.string.fs_disallowed)).append("\n");
444         }
445 
446         if (!hasWifi && !hasTelephony && !hasBluetooth) {
447             sb.append(getResources().getString(R.string.fs_missing_wifi_telephony)).append("\n");
448         }
449 
450         String warnings = sb.toString().trim();
451         if (warnings == null || "".equals(warnings)) {
452             ((TextView) (findViewById(R.id.fs_warnings))).setVisibility(View.GONE);
453         } else {
454             ((TextView) (findViewById(R.id.fs_warnings))).setText(warnings);
455         }
456     }
457 }
458