1 /*
2  * Copyright (C) 2015 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 package com.android.compatibility.common.deviceinfo;
17 
18 import android.content.pm.FeatureInfo;
19 import android.content.pm.PackageManager;
20 import android.os.Build;
21 
22 import com.android.compatibility.common.util.DeviceInfoStore;
23 
24 import java.lang.reflect.Field;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.util.ArrayList;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
31 
32 /**
33  * Feature device info collector.
34  */
35 public final class FeatureDeviceInfo extends DeviceInfo {
36 
37     @Override
collectDeviceInfo(DeviceInfoStore store)38     protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
39         PackageManager packageManager =
40                 getInstrumentation().getContext().getPackageManager();
41         store.startArray("feature");
42 
43         List<String> sdkFeatures = getPackageManagerFeatures();
44 
45         FeatureInfo[] featureInfos = packageManager.getSystemAvailableFeatures();
46         if (featureInfos != null) {
47             for (FeatureInfo featureInfo : featureInfos) {
48                 if (featureInfo.name != null) {
49                     // Check if this feature is a "sdk" feature.
50                     String type = "other";
51                     if (sdkFeatures.contains(featureInfo.name)) {
52                         type = "sdk";
53                         sdkFeatures.remove(featureInfo.name);
54                     }
55                     // Add the feature version if avaiable.
56                     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
57                         int version = featureInfo.version;
58                         addFeature(store, featureInfo.name, type, true, version);
59                     } else {
60                         addFeature(store, featureInfo.name, type, true);
61                     }
62                 }
63             }
64         }
65 
66         // Store the remaining "sdk" features not avaiable on this device.
67         for (String featureName : sdkFeatures) {
68             boolean hasFeature = packageManager.hasSystemFeature(featureName);
69             addFeature(store, featureName, "sdk", hasFeature);
70         }
71 
72         store.endArray();
73     }
74 
75     /**
76      * Use reflection to get the features defined by the SDK. If there are
77      * features that do not fit the convention of starting with "FEATURE_"
78      * then they will still be shown under the "Other Features" section.
79      *
80      * @return list of feature names from sdk
81      */
getPackageManagerFeatures()82     private List<String> getPackageManagerFeatures() {
83         try {
84             List<String> features = new ArrayList<String>();
85             Field[] fields = PackageManager.class.getFields();
86             for (Field field : fields) {
87                 if (field.getName().startsWith("FEATURE_")) {
88                     String feature = (String) field.get(null);
89                     features.add(feature);
90                 }
91             }
92             return features;
93         } catch (IllegalAccessException illegalAccess) {
94             throw new RuntimeException(illegalAccess);
95         }
96     }
97 
addFeature( DeviceInfoStore store, String name, String type, boolean available)98     private void addFeature(
99             DeviceInfoStore store,
100             String name,
101             String type,
102             boolean available) throws Exception {
103         store.startGroup();
104         store.addResult("name", name);
105         store.addResult("type", type);
106         store.addResult("available", available);
107         store.endGroup();
108     }
109 
addFeature( DeviceInfoStore store, String name, String type, boolean available, int version)110     private void addFeature(
111             DeviceInfoStore store,
112             String name,
113             String type,
114             boolean available,
115             int version) throws Exception {
116         store.startGroup();
117         store.addResult("name", name);
118         store.addResult("type", type);
119         store.addResult("available", available);
120         store.addResult("version", version);
121         store.endGroup();
122     }
123 }
124