Home | History | Annotate | Download | only in xml
      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 package com.android.sdklib.xml;
     18 
     19 import com.android.resources.Keyboard;
     20 import com.android.resources.Navigation;
     21 import com.android.resources.TouchScreen;
     22 
     23 import java.util.ArrayList;
     24 import java.util.Set;
     25 import java.util.TreeSet;
     26 
     27 /**
     28  * Class containing the manifest info obtained during the parsing.
     29  */
     30 public final class ManifestData {
     31 
     32     /**
     33      * Value returned by {@link #getMinSdkVersion()} when the value of the minSdkVersion attribute
     34      * in the manifest is a codename and not an integer value.
     35      */
     36     public final static int MIN_SDK_CODENAME = 0;
     37 
     38     /**
     39      * Value returned by {@link #getGlEsVersion()} when there are no <uses-feature> node with the
     40      * attribute glEsVersion set.
     41      */
     42     public final static int GL_ES_VERSION_NOT_SET = -1;
     43 
     44     /** Application package */
     45     String mPackage;
     46     /** Application version Code, null if the attribute is not present. */
     47     Integer mVersionCode = null;
     48     /** List of all activities */
     49     final ArrayList<Activity> mActivities = new ArrayList<Activity>();
     50     /** Launcher activity */
     51     Activity mLauncherActivity = null;
     52     /** list of process names declared by the manifest */
     53     Set<String> mProcesses = null;
     54     /** debuggable attribute value. If null, the attribute is not present. */
     55     Boolean mDebuggable = null;
     56     /** API level requirement. if null the attribute was not present. */
     57     private String mMinSdkVersionString = null;
     58     /** API level requirement. Default is 1 even if missing. If value is a codename, then it'll be
     59      * 0 instead. */
     60     private int mMinSdkVersion = 1;
     61     private int mTargetSdkVersion = 0;
     62     /** List of all instrumentations declared by the manifest */
     63     final ArrayList<Instrumentation> mInstrumentations =
     64         new ArrayList<Instrumentation>();
     65     /** List of all libraries in use declared by the manifest */
     66     final ArrayList<UsesLibrary> mLibraries = new ArrayList<UsesLibrary>();
     67     /** List of all feature in use declared by the manifest */
     68     final ArrayList<UsesFeature> mFeatures = new ArrayList<UsesFeature>();
     69 
     70     SupportsScreens mSupportsScreensFromManifest;
     71     SupportsScreens mSupportsScreensValues;
     72     UsesConfiguration mUsesConfiguration;
     73 
     74     /**
     75      * Instrumentation info obtained from manifest
     76      */
     77     public final static class Instrumentation {
     78         private final String mName;
     79         private final String mTargetPackage;
     80 
     81         Instrumentation(String name, String targetPackage) {
     82             mName = name;
     83             mTargetPackage = targetPackage;
     84         }
     85 
     86         /**
     87          * Returns the fully qualified instrumentation class name
     88          */
     89         public String getName() {
     90             return mName;
     91         }
     92 
     93         /**
     94          * Returns the Android app package that is the target of this instrumentation
     95          */
     96         public String getTargetPackage() {
     97             return mTargetPackage;
     98         }
     99     }
    100 
    101     /**
    102      * Activity info obtained from the manifest.
    103      */
    104     public final static class Activity {
    105         private final String mName;
    106         private final boolean mIsExported;
    107         private boolean mHasAction = false;
    108         private boolean mHasMainAction = false;
    109         private boolean mHasLauncherCategory = false;
    110 
    111         public Activity(String name, boolean exported) {
    112             mName = name;
    113             mIsExported = exported;
    114         }
    115 
    116         public String getName() {
    117             return mName;
    118         }
    119 
    120         public boolean isExported() {
    121             return mIsExported;
    122         }
    123 
    124         public boolean hasAction() {
    125             return mHasAction;
    126         }
    127 
    128         public boolean isHomeActivity() {
    129             return mHasMainAction && mHasLauncherCategory;
    130         }
    131 
    132         void setHasAction(boolean hasAction) {
    133             mHasAction = hasAction;
    134         }
    135 
    136         /** If the activity doesn't yet have a filter set for the launcher, this resets both
    137          * flags. This is to handle multiple intent-filters where one could have the valid
    138          * action, and another one of the valid category.
    139          */
    140         void resetIntentFilter() {
    141             if (isHomeActivity() == false) {
    142                 mHasMainAction = mHasLauncherCategory = false;
    143             }
    144         }
    145 
    146         void setHasMainAction(boolean hasMainAction) {
    147             mHasMainAction = hasMainAction;
    148         }
    149 
    150         void setHasLauncherCategory(boolean hasLauncherCategory) {
    151             mHasLauncherCategory = hasLauncherCategory;
    152         }
    153     }
    154 
    155     /**
    156      * Class representing the <code>supports-screens</code> node in the manifest.
    157      * By default, all the getters will return null if there was no value defined in the manifest.
    158      *
    159      * To get an instance with all the actual values, use {@link #resolveSupportsScreensValues(int)}
    160      */
    161     public final static class SupportsScreens {
    162         private Boolean mResizeable;
    163         private Boolean mAnyDensity;
    164         private Boolean mSmallScreens;
    165         private Boolean mNormalScreens;
    166         private Boolean mLargeScreens;
    167 
    168         public SupportsScreens() {
    169         }
    170 
    171         /**
    172          * Instantiate an instance from a string. The string must have been created with
    173          * {@link #getEncodedValues()}.
    174          * @param value the string.
    175          */
    176         public SupportsScreens(String value) {
    177             String[] values = value.split("\\|");
    178 
    179             mAnyDensity = Boolean.valueOf(values[0]);
    180             mResizeable = Boolean.valueOf(values[1]);
    181             mSmallScreens = Boolean.valueOf(values[2]);
    182             mNormalScreens = Boolean.valueOf(values[3]);
    183             mLargeScreens = Boolean.valueOf(values[4]);
    184         }
    185 
    186         /**
    187          * Returns an instance of {@link SupportsScreens} initialized with the default values
    188          * based on the given targetSdkVersion.
    189          * @param targetSdkVersion
    190          */
    191         public static SupportsScreens getDefaultValues(int targetSdkVersion) {
    192             SupportsScreens result = new SupportsScreens();
    193 
    194             result.mNormalScreens = Boolean.TRUE;
    195             // Screen size and density became available in Android 1.5/API3, so before that
    196             // non normal screens were not supported by default. After they are considered
    197             // supported.
    198             result.mResizeable = result.mAnyDensity = result.mSmallScreens = result.mLargeScreens =
    199                 targetSdkVersion <= 3 ? Boolean.FALSE : Boolean.TRUE;
    200 
    201             return result;
    202         }
    203 
    204         /**
    205          * Returns a version of the receiver for which all values have been set, even if they
    206          * were not present in the manifest.
    207          * @param targetSdkVersion the target api level of the app, since this has an effect
    208          * on default values.
    209          */
    210         public SupportsScreens resolveSupportsScreensValues(int targetSdkVersion) {
    211             SupportsScreens result = getDefaultValues(targetSdkVersion);
    212 
    213             // Override the default with the existing values:
    214             if (mResizeable != null) result.mResizeable = mResizeable;
    215             if (mAnyDensity != null) result.mAnyDensity = mAnyDensity;
    216             if (mSmallScreens != null) result.mSmallScreens = mSmallScreens;
    217             if (mNormalScreens != null) result.mNormalScreens = mNormalScreens;
    218             if (mLargeScreens != null) result.mLargeScreens = mLargeScreens;
    219 
    220             return result;
    221         }
    222 
    223         /**
    224          * returns the value of the <code>resizeable</code> attribute or null if not present.
    225          */
    226         public Boolean getResizeable() {
    227             return mResizeable;
    228         }
    229 
    230         void setResizeable(Boolean resizeable) {
    231             mResizeable = getConstantBoolean(resizeable);
    232         }
    233 
    234         /**
    235          * returns the value of the <code>anyDensity</code> attribute or null if not present.
    236          */
    237         public Boolean getAnyDensity() {
    238             return mAnyDensity;
    239         }
    240 
    241         void setAnyDensity(Boolean anyDensity) {
    242             mAnyDensity = getConstantBoolean(anyDensity);
    243         }
    244 
    245         /**
    246          * returns the value of the <code>smallScreens</code> attribute or null if not present.
    247          */
    248         public Boolean getSmallScreens() {
    249             return mSmallScreens;
    250         }
    251 
    252         void setSmallScreens(Boolean smallScreens) {
    253             mSmallScreens = getConstantBoolean(smallScreens);
    254         }
    255 
    256         /**
    257          * returns the value of the <code>normalScreens</code> attribute or null if not present.
    258          */
    259         public Boolean getNormalScreens() {
    260             return mNormalScreens;
    261         }
    262 
    263         void setNormalScreens(Boolean normalScreens) {
    264             mNormalScreens = getConstantBoolean(normalScreens);
    265         }
    266 
    267         /**
    268          * returns the value of the <code>largeScreens</code> attribute or null if not present.
    269          */
    270         public Boolean getLargeScreens() {
    271             return mLargeScreens;
    272         }
    273 
    274         void setLargeScreens(Boolean largeScreens) {
    275             mLargeScreens = getConstantBoolean(largeScreens);
    276         }
    277 
    278         /**
    279          * Returns either {@link Boolean#TRUE} or {@link Boolean#FALSE} based on the value of
    280          * the given Boolean object.
    281          */
    282         private Boolean getConstantBoolean(Boolean v) {
    283             if (v != null) {
    284                 if (v.equals(Boolean.TRUE)) {
    285                     return Boolean.TRUE;
    286                 } else {
    287                     return Boolean.FALSE;
    288                 }
    289             }
    290 
    291             return null;
    292         }
    293 
    294         @Override
    295         public boolean equals(Object obj) {
    296             if (obj instanceof SupportsScreens) {
    297                 SupportsScreens support = (SupportsScreens) obj;
    298                 // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
    299                 // (or null), we can simply check they are identical and not bother with
    300                 // calling equals (which would require to check != null.
    301                 // see #getConstanntBoolean(Boolean)
    302                 return mResizeable    == support.mResizeable &&
    303                        mAnyDensity    == support.mAnyDensity &&
    304                        mSmallScreens  == support.mSmallScreens &&
    305                        mNormalScreens == support.mNormalScreens &&
    306                        mLargeScreens  == support.mLargeScreens;
    307             }
    308 
    309             return false;
    310         }
    311 
    312         /* Override hashCode, mostly to make Eclipse happy and not warn about it.
    313          * And if you ever put this in a Map or Set, it will avoid surprises. */
    314         @Override
    315         public int hashCode() {
    316             final int prime = 31;
    317             int result = 1;
    318             result = prime * result + ((mAnyDensity    == null) ? 0 : mAnyDensity.hashCode());
    319             result = prime * result + ((mLargeScreens  == null) ? 0 : mLargeScreens.hashCode());
    320             result = prime * result + ((mNormalScreens == null) ? 0 : mNormalScreens.hashCode());
    321             result = prime * result + ((mResizeable    == null) ? 0 : mResizeable.hashCode());
    322             result = prime * result + ((mSmallScreens  == null) ? 0 : mSmallScreens.hashCode());
    323             return result;
    324         }
    325 
    326         /**
    327          * Returns true if the two instances support the same screen sizes.
    328          * This is similar to {@link #equals(Object)} except that it ignores the values of
    329          * {@link #getAnyDensity()} and {@link #getResizeable()}.
    330          * @param support the other instance to compare to.
    331          * @return true if the two instances support the same screen sizes.
    332          */
    333         public boolean hasSameScreenSupportAs(SupportsScreens support) {
    334             // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
    335             // (or null), we can simply check they are identical and not bother with
    336             // calling equals (which would require to check != null.
    337             // see #getConstanntBoolean(Boolean)
    338 
    339             // This only checks that matter here are the screen sizes. resizeable and anyDensity
    340             // are not checked.
    341             return  mSmallScreens == support.mSmallScreens &&
    342                     mNormalScreens == support.mNormalScreens &&
    343                     mLargeScreens == support.mLargeScreens;
    344         }
    345 
    346         /**
    347          * Returns true if the two instances have strictly different screen size support.
    348          * This means that there is no screen size that they both support.
    349          * @param support the other instance to compare to.
    350          * @return true if they are stricly different.
    351          */
    352         public boolean hasStrictlyDifferentScreenSupportAs(SupportsScreens support) {
    353             // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
    354             // (or null), we can simply check they are identical and not bother with
    355             // calling equals (which would require to check != null.
    356             // see #getConstanntBoolean(Boolean)
    357 
    358             // This only checks that matter here are the screen sizes. resizeable and anyDensity
    359             // are not checked.
    360             return (mSmallScreens != Boolean.TRUE || support.mSmallScreens != Boolean.TRUE) &&
    361                     (mNormalScreens != Boolean.TRUE || support.mNormalScreens != Boolean.TRUE) &&
    362                     (mLargeScreens != Boolean.TRUE || support.mLargeScreens != Boolean.TRUE);
    363         }
    364 
    365         /**
    366          * Comparison of 2 Supports-screens. This only uses screen sizes (ignores resizeable and
    367          * anyDensity), and considers that
    368          * {@link #hasStrictlyDifferentScreenSupportAs(SupportsScreens)} returns true and
    369          * {@link #overlapWith(SupportsScreens)} returns false.
    370          * @throws IllegalArgumentException if the two instanced are not strictly different or
    371          * overlap each other
    372          * @see #hasStrictlyDifferentScreenSupportAs(SupportsScreens)
    373          * @see #overlapWith(SupportsScreens)
    374          */
    375         public int compareScreenSizesWith(SupportsScreens o) {
    376             if (hasStrictlyDifferentScreenSupportAs(o) == false) {
    377                 throw new IllegalArgumentException("The two instances are not strictly different.");
    378             }
    379             if (overlapWith(o)) {
    380                 throw new IllegalArgumentException("The two instances overlap each other.");
    381             }
    382 
    383             int comp = mLargeScreens.compareTo(o.mLargeScreens);
    384             if (comp != 0) return comp;
    385 
    386             comp = mNormalScreens.compareTo(o.mNormalScreens);
    387             if (comp != 0) return comp;
    388 
    389             comp = mSmallScreens.compareTo(o.mSmallScreens);
    390             if (comp != 0) return comp;
    391 
    392             return 0;
    393         }
    394 
    395         /**
    396          * Returns a string encoding of the content of the instance. This string can be used to
    397          * instantiate a {@link SupportsScreens} object through
    398          * {@link #SupportsScreens(String)}.
    399          */
    400         public String getEncodedValues() {
    401             return String.format("%1$s|%2$s|%3$s|%4$s|%5$s",
    402                     mAnyDensity, mResizeable, mSmallScreens, mNormalScreens, mLargeScreens);
    403         }
    404 
    405         @Override
    406         public String toString() {
    407             StringBuilder sb = new StringBuilder();
    408 
    409             boolean alreadyOutputSomething = false;
    410 
    411             if (Boolean.TRUE.equals(mSmallScreens)) {
    412                 alreadyOutputSomething = true;
    413                 sb.append("small");
    414             }
    415 
    416             if (Boolean.TRUE.equals(mNormalScreens)) {
    417                 if (alreadyOutputSomething) {
    418                     sb.append(", ");
    419                 }
    420                 alreadyOutputSomething = true;
    421                 sb.append("normal");
    422             }
    423 
    424             if (Boolean.TRUE.equals(mLargeScreens)) {
    425                 if (alreadyOutputSomething) {
    426                     sb.append(", ");
    427                 }
    428                 alreadyOutputSomething = true;
    429                 sb.append("large");
    430             }
    431 
    432             if (alreadyOutputSomething == false) {
    433                 sb.append("<none>");
    434             }
    435 
    436             return sb.toString();
    437         }
    438 
    439         /**
    440          * Returns true if the two instance overlap with each other.
    441          * This can happen if one instances supports a size, when the other instance doesn't while
    442          * supporting a size above and a size below.
    443          * @param otherSS the other supports-screens to compare to.
    444          */
    445         public boolean overlapWith(SupportsScreens otherSS) {
    446             if (mSmallScreens == null || mNormalScreens == null || mLargeScreens == null ||
    447                     otherSS.mSmallScreens == null || otherSS.mNormalScreens == null ||
    448                     otherSS.mLargeScreens == null) {
    449                 throw new IllegalArgumentException("Some screen sizes Boolean are not initialized");
    450             }
    451 
    452             if (mSmallScreens == Boolean.TRUE && mNormalScreens == Boolean.FALSE &&
    453                     mLargeScreens == Boolean.TRUE) {
    454                 return otherSS.mNormalScreens == Boolean.TRUE;
    455             }
    456 
    457             if (otherSS.mSmallScreens == Boolean.TRUE && otherSS.mNormalScreens == Boolean.FALSE &&
    458                     otherSS.mLargeScreens == Boolean.TRUE) {
    459                 return mNormalScreens == Boolean.TRUE;
    460             }
    461 
    462             return false;
    463         }
    464     }
    465 
    466     /**
    467      * Class representing a <code>uses-library</code> node in the manifest.
    468      */
    469     public final static class UsesLibrary {
    470         String mName;
    471         Boolean mRequired = Boolean.TRUE; // default is true even if missing
    472 
    473         public String getName() {
    474             return mName;
    475         }
    476 
    477         public Boolean getRequired() {
    478             return mRequired;
    479         }
    480     }
    481 
    482     /**
    483      * Class representing a <code>uses-feature</code> node in the manifest.
    484      */
    485     public final static class UsesFeature {
    486         String mName;
    487         int mGlEsVersion = 0;
    488         Boolean mRequired = Boolean.TRUE;  // default is true even if missing
    489 
    490         public String getName() {
    491             return mName;
    492         }
    493 
    494         /**
    495          * Returns the value of the glEsVersion attribute, or 0 if the attribute was not present.
    496          */
    497         public int getGlEsVersion() {
    498             return mGlEsVersion;
    499         }
    500 
    501         public Boolean getRequired() {
    502             return mRequired;
    503         }
    504     }
    505 
    506     /**
    507      * Class representing the <code>uses-configuration</code> node in the manifest.
    508      */
    509     public final static class UsesConfiguration {
    510         Boolean mReqFiveWayNav;
    511         Boolean mReqHardKeyboard;
    512         Keyboard mReqKeyboardType;
    513         TouchScreen mReqTouchScreen;
    514         Navigation mReqNavigation;
    515 
    516         /**
    517          * returns the value of the <code>reqFiveWayNav</code> attribute or null if not present.
    518          */
    519         public Boolean getReqFiveWayNav() {
    520             return mReqFiveWayNav;
    521         }
    522 
    523         /**
    524          * returns the value of the <code>reqNavigation</code> attribute or null if not present.
    525          */
    526         public Navigation getReqNavigation() {
    527             return mReqNavigation;
    528         }
    529 
    530         /**
    531          * returns the value of the <code>reqHardKeyboard</code> attribute or null if not present.
    532          */
    533         public Boolean getReqHardKeyboard() {
    534             return mReqHardKeyboard;
    535         }
    536 
    537         /**
    538          * returns the value of the <code>reqKeyboardType</code> attribute or null if not present.
    539          */
    540         public Keyboard getReqKeyboardType() {
    541             return mReqKeyboardType;
    542         }
    543 
    544         /**
    545          * returns the value of the <code>reqTouchScreen</code> attribute or null if not present.
    546          */
    547         public TouchScreen getReqTouchScreen() {
    548             return mReqTouchScreen;
    549         }
    550     }
    551 
    552     /**
    553      * Returns the package defined in the manifest, if found.
    554      * @return The package name or null if not found.
    555      */
    556     public String getPackage() {
    557         return mPackage;
    558     }
    559 
    560     /**
    561      * Returns the versionCode value defined in the manifest, if found, null otherwise.
    562      * @return the versionCode or null if not found.
    563      */
    564     public Integer getVersionCode() {
    565         return mVersionCode;
    566     }
    567 
    568     /**
    569      * Returns the list of activities found in the manifest.
    570      * @return An array of fully qualified class names, or empty if no activity were found.
    571      */
    572     public Activity[] getActivities() {
    573         return mActivities.toArray(new Activity[mActivities.size()]);
    574     }
    575 
    576     /**
    577      * Returns the name of one activity found in the manifest, that is configured to show
    578      * up in the HOME screen.
    579      * @return the fully qualified name of a HOME activity or null if none were found.
    580      */
    581     public Activity getLauncherActivity() {
    582         return mLauncherActivity;
    583     }
    584 
    585     /**
    586      * Returns the list of process names declared by the manifest.
    587      */
    588     public String[] getProcesses() {
    589         if (mProcesses != null) {
    590             return mProcesses.toArray(new String[mProcesses.size()]);
    591         }
    592 
    593         return new String[0];
    594     }
    595 
    596     /**
    597      * Returns the <code>debuggable</code> attribute value or null if it is not set.
    598      */
    599     public Boolean getDebuggable() {
    600         return mDebuggable;
    601     }
    602 
    603     /**
    604      * Returns the <code>minSdkVersion</code> attribute, or null if it's not set.
    605      */
    606     public String getMinSdkVersionString() {
    607         return mMinSdkVersionString;
    608     }
    609 
    610     /**
    611      * Sets the value of the <code>minSdkVersion</code> attribute.
    612      * @param minSdkVersion the string value of the attribute in the manifest.
    613      */
    614     public void setMinSdkVersionString(String minSdkVersion) {
    615         mMinSdkVersionString = minSdkVersion;
    616         if (mMinSdkVersionString != null) {
    617             try {
    618                 mMinSdkVersion = Integer.parseInt(mMinSdkVersionString);
    619             } catch (NumberFormatException e) {
    620                 mMinSdkVersion = MIN_SDK_CODENAME;
    621             }
    622         }
    623     }
    624 
    625     /**
    626      * Returns the <code>minSdkVersion</code> attribute, or 0 if it's not set or is a codename.
    627      * @see #getMinSdkVersionString()
    628      */
    629     public int getMinSdkVersion() {
    630         return mMinSdkVersion;
    631     }
    632 
    633 
    634     /**
    635      * Sets the value of the <code>minSdkVersion</code> attribute.
    636      * @param targetSdkVersion the string value of the attribute in the manifest.
    637      */
    638     public void setTargetSdkVersionString(String targetSdkVersion) {
    639         if (targetSdkVersion != null) {
    640             try {
    641                 mTargetSdkVersion = Integer.parseInt(targetSdkVersion);
    642             } catch (NumberFormatException e) {
    643                 // keep the value at 0.
    644             }
    645         }
    646     }
    647 
    648     /**
    649      * Returns the <code>targetSdkVersion</code> attribute, or the same value as
    650      * {@link #getMinSdkVersion()} if it was not set in the manifest.
    651      */
    652     public int getTargetSdkVersion() {
    653         if (mTargetSdkVersion == 0) {
    654             return getMinSdkVersion();
    655         }
    656 
    657         return mTargetSdkVersion;
    658     }
    659 
    660     /**
    661      * Returns the list of instrumentations found in the manifest.
    662      * @return An array of {@link Instrumentation}, or empty if no instrumentations were
    663      * found.
    664      */
    665     public Instrumentation[] getInstrumentations() {
    666         return mInstrumentations.toArray(new Instrumentation[mInstrumentations.size()]);
    667     }
    668 
    669     /**
    670      * Returns the list of libraries in use found in the manifest.
    671      * @return An array of {@link UsesLibrary} objects, or empty if no libraries were found.
    672      */
    673     public UsesLibrary[] getUsesLibraries() {
    674         return mLibraries.toArray(new UsesLibrary[mLibraries.size()]);
    675     }
    676 
    677     /**
    678      * Returns the list of features in use found in the manifest.
    679      * @return An array of {@link UsesFeature} objects, or empty if no libraries were found.
    680      */
    681     public UsesFeature[] getUsesFeatures() {
    682         return mFeatures.toArray(new UsesFeature[mFeatures.size()]);
    683     }
    684 
    685     /**
    686      * Returns the glEsVersion from a <uses-feature> or {@link #GL_ES_VERSION_NOT_SET} if not set.
    687      */
    688     public int getGlEsVersion() {
    689         for (UsesFeature feature : mFeatures) {
    690             if (feature.mGlEsVersion > 0) {
    691                 return feature.mGlEsVersion;
    692             }
    693         }
    694         return GL_ES_VERSION_NOT_SET;
    695     }
    696 
    697     /**
    698      * Returns the {@link SupportsScreens} object representing the <code>supports-screens</code>
    699      * node, or null if the node doesn't exist at all.
    700      * Some values in the {@link SupportsScreens} instance maybe null, indicating that they
    701      * were not present in the manifest. To get an instance that contains the values, as seen
    702      * by the Android platform when the app is running, use {@link #getSupportsScreensValues()}.
    703      */
    704     public SupportsScreens getSupportsScreensFromManifest() {
    705         return mSupportsScreensFromManifest;
    706     }
    707 
    708     /**
    709      * Returns an always non-null instance of {@link SupportsScreens} that's been initialized with
    710      * the default values, and the values from the manifest.
    711      * The default values depends on the manifest values for minSdkVersion and targetSdkVersion.
    712      */
    713     public synchronized SupportsScreens getSupportsScreensValues() {
    714         if (mSupportsScreensValues == null) {
    715             if (mSupportsScreensFromManifest == null) {
    716                 mSupportsScreensValues = SupportsScreens.getDefaultValues(getTargetSdkVersion());
    717             } else {
    718                 // get a SupportsScreen that replace the missing values with default values.
    719                 mSupportsScreensValues = mSupportsScreensFromManifest.resolveSupportsScreensValues(
    720                         getTargetSdkVersion());
    721             }
    722         }
    723 
    724         return mSupportsScreensValues;
    725     }
    726 
    727     /**
    728      * Returns the {@link UsesConfiguration} object representing the <code>uses-configuration</code>
    729      * node, or null if the node doesn't exist at all.
    730      */
    731     public UsesConfiguration getUsesConfiguration() {
    732         return mUsesConfiguration;
    733     }
    734 
    735     void addProcessName(String processName) {
    736         if (mProcesses == null) {
    737             mProcesses = new TreeSet<String>();
    738         }
    739 
    740         if (processName.startsWith(":")) {
    741             mProcesses.add(mPackage + processName);
    742         } else {
    743             mProcesses.add(processName);
    744         }
    745     }
    746 
    747 }
    748