Home | History | Annotate | Download | only in content
      1 /*
      2  * Copyright (C) 2014 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 android.content;
     18 
     19 import android.app.Activity;
     20 import android.app.admin.DevicePolicyManager;
     21 import android.content.pm.ApplicationInfo;
     22 import android.content.pm.PackageManager;
     23 import android.content.pm.PackageManager.NameNotFoundException;
     24 import android.content.res.TypedArray;
     25 import android.content.res.XmlResourceParser;
     26 import android.os.Bundle;
     27 import android.os.PersistableBundle;
     28 import android.os.RemoteException;
     29 import android.service.restrictions.RestrictionsReceiver;
     30 import android.util.AttributeSet;
     31 import android.util.Log;
     32 import android.util.Xml;
     33 
     34 import com.android.internal.R;
     35 
     36 import org.xmlpull.v1.XmlPullParser;
     37 import org.xmlpull.v1.XmlPullParserException;
     38 
     39 import java.io.IOException;
     40 import java.util.ArrayList;
     41 import java.util.List;
     42 
     43 /**
     44  * Provides a mechanism for apps to query restrictions imposed by an entity that
     45  * manages the user. Apps can also send permission requests to a local or remote
     46  * device administrator to override default app-specific restrictions or any other
     47  * operation that needs explicit authorization from the administrator.
     48  * <p>
     49  * Apps can expose a set of restrictions via an XML file specified in the manifest.
     50  * <p>
     51  * If the user has an active Restrictions Provider, dynamic requests can be made in
     52  * addition to the statically imposed restrictions. Dynamic requests are app-specific
     53  * and can be expressed via a predefined set of request types.
     54  * <p>
     55  * The RestrictionsManager forwards the dynamic requests to the active
     56  * Restrictions Provider. The Restrictions Provider can respond back to requests by calling
     57  * {@link #notifyPermissionResponse(String, PersistableBundle)}, when
     58  * a response is received from the administrator of the device or user.
     59  * The response is relayed back to the application via a protected broadcast,
     60  * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
     61  * <p>
     62  * Static restrictions are specified by an XML file referenced by a meta-data attribute
     63  * in the manifest. This enables applications as well as any web administration consoles
     64  * to be able to read the list of available restrictions from the apk.
     65  * <p>
     66  * The syntax of the XML format is as follows:
     67  * <pre>
     68  * &lt;?xml version="1.0" encoding="utf-8"?&gt;
     69  * &lt;restrictions xmlns:android="http://schemas.android.com/apk/res/android" &gt;
     70  *     &lt;restriction
     71  *         android:key="string"
     72  *         android:title="string resource"
     73  *         android:restrictionType=["bool" | "string" | "integer"
     74  *                                         | "choice" | "multi-select" | "hidden"]
     75  *         android:description="string resource"
     76  *         android:entries="string-array resource"
     77  *         android:entryValues="string-array resource"
     78  *         android:defaultValue="reference"
     79  *         /&gt;
     80  *     &lt;restriction ... /&gt;
     81  *     ...
     82  * &lt;/restrictions&gt;
     83  * </pre>
     84  * <p>
     85  * The attributes for each restriction depend on the restriction type.
     86  * <p>
     87  * <ul>
     88  * <li><code>key</code>, <code>title</code> and <code>restrictionType</code> are mandatory.</li>
     89  * <li><code>entries</code> and <code>entryValues</code> are required if <code>restrictionType
     90  * </code> is <code>choice</code> or <code>multi-select</code>.</li>
     91  * <li><code>defaultValue</code> is optional and its type depends on the
     92  * <code>restrictionType</code></li>
     93  * <li><code>hidden</code> type must have a <code>defaultValue</code> and will
     94  * not be shown to the administrator. It can be used to pass along data that cannot be modified,
     95  * such as a version code.</li>
     96  * <li><code>description</code> is meant to describe the restriction in more detail to the
     97  * administrator controlling the values, if the title is not sufficient.</li>
     98  * </ul>
     99  * <p>
    100  * In your manifest's <code>application</code> section, add the meta-data tag to point to
    101  * the restrictions XML file as shown below:
    102  * <pre>
    103  * &lt;application ... &gt;
    104  *     &lt;meta-data android:name="android.content.APP_RESTRICTIONS"
    105  *                   android:resource="@xml/app_restrictions" /&gt;
    106  *     ...
    107  * &lt;/application&gt;
    108  * </pre>
    109  *
    110  * @see RestrictionEntry
    111  * @see RestrictionsReceiver
    112  * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)
    113  * @see DevicePolicyManager#setApplicationRestrictions(ComponentName, String, Bundle)
    114  */
    115 public class RestrictionsManager {
    116 
    117     private static final String TAG = "RestrictionsManager";
    118 
    119     /**
    120      * Broadcast intent delivered when a response is received for a permission request. The
    121      * application should not interrupt the user by coming to the foreground if it isn't
    122      * currently in the foreground. It can either post a notification informing
    123      * the user of the response or wait until the next time the user launches the app.
    124      * <p>
    125      * For instance, if the user requested permission to make an in-app purchase,
    126      * the app can post a notification that the request had been approved or denied.
    127      * <p>
    128      * The broadcast Intent carries the following extra:
    129      * {@link #EXTRA_RESPONSE_BUNDLE}.
    130      */
    131     public static final String ACTION_PERMISSION_RESPONSE_RECEIVED =
    132             "android.content.action.PERMISSION_RESPONSE_RECEIVED";
    133 
    134     /**
    135      * Broadcast intent sent to the Restrictions Provider to handle a permission request from
    136      * an app. It will have the following extras: {@link #EXTRA_PACKAGE_NAME},
    137      * {@link #EXTRA_REQUEST_TYPE}, {@link #EXTRA_REQUEST_ID} and {@link #EXTRA_REQUEST_BUNDLE}.
    138      * The Restrictions Provider will handle the request and respond back to the
    139      * RestrictionsManager, when a response is available, by calling
    140      * {@link #notifyPermissionResponse}.
    141      * <p>
    142      * The BroadcastReceiver must require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN}
    143      * permission to ensure that only the system can send the broadcast.
    144      */
    145     public static final String ACTION_REQUEST_PERMISSION =
    146             "android.content.action.REQUEST_PERMISSION";
    147 
    148     /**
    149      * Activity intent that is optionally implemented by the Restrictions Provider package
    150      * to challenge for an administrator PIN or password locally on the device. Apps will
    151      * call this intent using {@link Activity#startActivityForResult}. On a successful
    152      * response, {@link Activity#onActivityResult} will return a resultCode of
    153      * {@link Activity#RESULT_OK}.
    154      * <p>
    155      * The intent must contain {@link #EXTRA_REQUEST_BUNDLE} as an extra and the bundle must
    156      * contain at least {@link #REQUEST_KEY_MESSAGE} for the activity to display.
    157      * <p>
    158      * @see #createLocalApprovalIntent()
    159      */
    160     public static final String ACTION_REQUEST_LOCAL_APPROVAL =
    161             "android.content.action.REQUEST_LOCAL_APPROVAL";
    162 
    163     /**
    164      * The package name of the application making the request.
    165      * <p>
    166      * Type: String
    167      */
    168     public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
    169 
    170     /**
    171      * The request type passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
    172      * <p>
    173      * Type: String
    174      */
    175     public static final String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
    176 
    177     /**
    178      * The request ID passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
    179      * <p>
    180      * Type: String
    181      */
    182     public static final String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID";
    183 
    184     /**
    185      * The request bundle passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
    186      * <p>
    187      * Type: {@link PersistableBundle}
    188      */
    189     public static final String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
    190 
    191     /**
    192      * Contains a response from the administrator for specific request.
    193      * The bundle contains the following information, at least:
    194      * <ul>
    195      * <li>{@link #REQUEST_KEY_ID}: The request ID.</li>
    196      * <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li>
    197      * </ul>
    198      * <p>
    199      * Type: {@link PersistableBundle}
    200      */
    201     public static final String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
    202 
    203     /**
    204      * Request type for a simple question, with a possible title and icon.
    205      * <p>
    206      * Required keys are: {@link #REQUEST_KEY_MESSAGE}
    207      * <p>
    208      * Optional keys are
    209      * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
    210      * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
    211      */
    212     public static final String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
    213 
    214     /**
    215      * Key for request ID contained in the request bundle.
    216      * <p>
    217      * App-generated request ID to identify the specific request when receiving
    218      * a response. This value is returned in the {@link #EXTRA_RESPONSE_BUNDLE}.
    219      * <p>
    220      * Type: String
    221      */
    222     public static final String REQUEST_KEY_ID = "android.request.id";
    223 
    224     /**
    225      * Key for request data contained in the request bundle.
    226      * <p>
    227      * Optional, typically used to identify the specific data that is being referred to,
    228      * such as the unique identifier for a movie or book. This is not used for display
    229      * purposes and is more like a cookie. This value is returned in the
    230      * {@link #EXTRA_RESPONSE_BUNDLE}.
    231      * <p>
    232      * Type: String
    233      */
    234     public static final String REQUEST_KEY_DATA = "android.request.data";
    235 
    236     /**
    237      * Key for request title contained in the request bundle.
    238      * <p>
    239      * Optional, typically used as the title of any notification or dialog presented
    240      * to the administrator who approves the request.
    241      * <p>
    242      * Type: String
    243      */
    244     public static final String REQUEST_KEY_TITLE = "android.request.title";
    245 
    246     /**
    247      * Key for request message contained in the request bundle.
    248      * <p>
    249      * Required, shown as the actual message in a notification or dialog presented
    250      * to the administrator who approves the request.
    251      * <p>
    252      * Type: String
    253      */
    254     public static final String REQUEST_KEY_MESSAGE = "android.request.mesg";
    255 
    256     /**
    257      * Key for request icon contained in the request bundle.
    258      * <p>
    259      * Optional, shown alongside the request message presented to the administrator
    260      * who approves the request. The content must be a compressed image such as a
    261      * PNG or JPEG, as a byte array.
    262      * <p>
    263      * Type: byte[]
    264      */
    265     public static final String REQUEST_KEY_ICON = "android.request.icon";
    266 
    267     /**
    268      * Key for request approval button label contained in the request bundle.
    269      * <p>
    270      * Optional, may be shown as a label on the positive button in a dialog or
    271      * notification presented to the administrator who approves the request.
    272      * <p>
    273      * Type: String
    274      */
    275     public static final String REQUEST_KEY_APPROVE_LABEL = "android.request.approve_label";
    276 
    277     /**
    278      * Key for request rejection button label contained in the request bundle.
    279      * <p>
    280      * Optional, may be shown as a label on the negative button in a dialog or
    281      * notification presented to the administrator who approves the request.
    282      * <p>
    283      * Type: String
    284      */
    285     public static final String REQUEST_KEY_DENY_LABEL = "android.request.deny_label";
    286 
    287     /**
    288      * Key for issuing a new request, contained in the request bundle. If this is set to true,
    289      * the Restrictions Provider must make a new request. If it is false or not specified, then
    290      * the Restrictions Provider can return a cached response that has the same requestId, if
    291      * available. If there's no cached response, it will issue a new one to the administrator.
    292      * <p>
    293      * Type: boolean
    294      */
    295     public static final String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
    296 
    297     /**
    298      * Key for the response result in the response bundle sent to the application, for a permission
    299      * request. It indicates the status of the request. In some cases an additional message might
    300      * be available in {@link #RESPONSE_KEY_MESSAGE}, to be displayed to the user.
    301      * <p>
    302      * Type: int
    303      * <p>
    304      * Possible values: {@link #RESULT_APPROVED}, {@link #RESULT_DENIED},
    305      * {@link #RESULT_NO_RESPONSE}, {@link #RESULT_UNKNOWN_REQUEST} or
    306      * {@link #RESULT_ERROR}.
    307      */
    308     public static final String RESPONSE_KEY_RESULT = "android.response.result";
    309 
    310     /**
    311      * Response result value indicating that the request was approved.
    312      */
    313     public static final int RESULT_APPROVED = 1;
    314 
    315     /**
    316      * Response result value indicating that the request was denied.
    317      */
    318     public static final int RESULT_DENIED = 2;
    319 
    320     /**
    321      * Response result value indicating that the request has not received a response yet.
    322      */
    323     public static final int RESULT_NO_RESPONSE = 3;
    324 
    325     /**
    326      * Response result value indicating that the request is unknown, when it's not a new
    327      * request.
    328      */
    329     public static final int RESULT_UNKNOWN_REQUEST = 4;
    330 
    331     /**
    332      * Response result value indicating an error condition. Additional error code might be available
    333      * in the response bundle, for the key {@link #RESPONSE_KEY_ERROR_CODE}. There might also be
    334      * an associated error message in the response bundle, for the key
    335      * {@link #RESPONSE_KEY_MESSAGE}.
    336      */
    337     public static final int RESULT_ERROR = 5;
    338 
    339     /**
    340      * Error code indicating that there was a problem with the request.
    341      * <p>
    342      * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle.
    343      */
    344     public static final int RESULT_ERROR_BAD_REQUEST = 1;
    345 
    346     /**
    347      * Error code indicating that there was a problem with the network.
    348      * <p>
    349      * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle.
    350      */
    351     public static final int RESULT_ERROR_NETWORK = 2;
    352 
    353     /**
    354      * Error code indicating that there was an internal error.
    355      * <p>
    356      * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle.
    357      */
    358     public static final int RESULT_ERROR_INTERNAL = 3;
    359 
    360     /**
    361      * Key for the optional error code in the response bundle sent to the application.
    362      * <p>
    363      * Type: int
    364      * <p>
    365      * Possible values: {@link #RESULT_ERROR_BAD_REQUEST}, {@link #RESULT_ERROR_NETWORK} or
    366      * {@link #RESULT_ERROR_INTERNAL}.
    367      */
    368     public static final String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
    369 
    370     /**
    371      * Key for the optional message in the response bundle sent to the application.
    372      * <p>
    373      * Type: String
    374      */
    375     public static final String RESPONSE_KEY_MESSAGE = "android.response.msg";
    376 
    377     /**
    378      * Key for the optional timestamp of when the administrator responded to the permission
    379      * request. It is an represented in milliseconds since January 1, 1970 00:00:00.0 UTC.
    380      * <p>
    381      * Type: long
    382      */
    383     public static final String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
    384 
    385     /**
    386      * Name of the meta-data entry in the manifest that points to the XML file containing the
    387      * application's available restrictions.
    388      * @see #getManifestRestrictions(String)
    389      */
    390     public static final String META_DATA_APP_RESTRICTIONS = "android.content.APP_RESTRICTIONS";
    391 
    392     private static final String TAG_RESTRICTION = "restriction";
    393 
    394     private final Context mContext;
    395     private final IRestrictionsManager mService;
    396 
    397     /**
    398      * @hide
    399      */
    400     public RestrictionsManager(Context context, IRestrictionsManager service) {
    401         mContext = context;
    402         mService = service;
    403     }
    404 
    405     /**
    406      * Returns any available set of application-specific restrictions applicable
    407      * to this application.
    408      * @return the application restrictions as a Bundle. Returns null if there
    409      * are no restrictions.
    410      */
    411     public Bundle getApplicationRestrictions() {
    412         try {
    413             if (mService != null) {
    414                 return mService.getApplicationRestrictions(mContext.getPackageName());
    415             }
    416         } catch (RemoteException re) {
    417             Log.w(TAG, "Couldn't reach service");
    418         }
    419         return null;
    420     }
    421 
    422     /**
    423      * Called by an application to check if there is an active Restrictions Provider. If
    424      * there isn't, {@link #requestPermission(String, String, PersistableBundle)} is not available.
    425      *
    426      * @return whether there is an active Restrictions Provider.
    427      */
    428     public boolean hasRestrictionsProvider() {
    429         try {
    430             if (mService != null) {
    431                 return mService.hasRestrictionsProvider();
    432             }
    433         } catch (RemoteException re) {
    434             Log.w(TAG, "Couldn't reach service");
    435         }
    436         return false;
    437     }
    438 
    439     /**
    440      * Called by an application to request permission for an operation. The contents of the
    441      * request are passed in a Bundle that contains several pieces of data depending on the
    442      * chosen request type.
    443      *
    444      * @param requestType The type of request. The type could be one of the
    445      * predefined types specified here or a custom type that the specific
    446      * Restrictions Provider might understand. For custom types, the type name should be
    447      * namespaced to avoid collisions with predefined types and types specified by
    448      * other Restrictions Providers.
    449      * @param requestId A unique id generated by the app that contains sufficient information
    450      * to identify the parameters of the request when it receives the id in the response.
    451      * @param request A PersistableBundle containing the data corresponding to the specified request
    452      * type. The keys for the data in the bundle depend on the request type.
    453      *
    454      * @throws IllegalArgumentException if any of the required parameters are missing.
    455      */
    456     public void requestPermission(String requestType, String requestId, PersistableBundle request) {
    457         if (requestType == null) {
    458             throw new NullPointerException("requestType cannot be null");
    459         }
    460         if (requestId == null) {
    461             throw new NullPointerException("requestId cannot be null");
    462         }
    463         if (request == null) {
    464             throw new NullPointerException("request cannot be null");
    465         }
    466         try {
    467             if (mService != null) {
    468                 mService.requestPermission(mContext.getPackageName(), requestType, requestId,
    469                         request);
    470             }
    471         } catch (RemoteException re) {
    472             Log.w(TAG, "Couldn't reach service");
    473         }
    474     }
    475 
    476     public Intent createLocalApprovalIntent() {
    477         try {
    478             if (mService != null) {
    479                 return mService.createLocalApprovalIntent();
    480             }
    481         } catch (RemoteException re) {
    482             Log.w(TAG, "Couldn't reach service");
    483         }
    484         return null;
    485     }
    486 
    487     /**
    488      * Called by the Restrictions Provider to deliver a response to an application.
    489      *
    490      * @param packageName the application to deliver the response to. Cannot be null.
    491      * @param response the bundle containing the response status, request ID and other information.
    492      *                 Cannot be null.
    493      *
    494      * @throws IllegalArgumentException if any of the required parameters are missing.
    495      */
    496     public void notifyPermissionResponse(String packageName, PersistableBundle response) {
    497         if (packageName == null) {
    498             throw new NullPointerException("packageName cannot be null");
    499         }
    500         if (response == null) {
    501             throw new NullPointerException("request cannot be null");
    502         }
    503         if (!response.containsKey(REQUEST_KEY_ID)) {
    504             throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
    505         }
    506         if (!response.containsKey(RESPONSE_KEY_RESULT)) {
    507             throw new IllegalArgumentException("RESPONSE_KEY_RESULT must be specified");
    508         }
    509         try {
    510             if (mService != null) {
    511                 mService.notifyPermissionResponse(packageName, response);
    512             }
    513         } catch (RemoteException re) {
    514             Log.w(TAG, "Couldn't reach service");
    515         }
    516     }
    517 
    518     /**
    519      * Parse and return the list of restrictions defined in the manifest for the specified
    520      * package, if any.
    521      *
    522      * @param packageName The application for which to fetch the restrictions list.
    523      * @return The list of RestrictionEntry objects created from the XML file specified
    524      * in the manifest, or null if none was specified.
    525      */
    526     public List<RestrictionEntry> getManifestRestrictions(String packageName) {
    527         ApplicationInfo appInfo = null;
    528         try {
    529             appInfo = mContext.getPackageManager().getApplicationInfo(packageName,
    530                     PackageManager.GET_META_DATA);
    531         } catch (NameNotFoundException pnfe) {
    532             throw new IllegalArgumentException("No such package " + packageName);
    533         }
    534         if (appInfo == null || !appInfo.metaData.containsKey(META_DATA_APP_RESTRICTIONS)) {
    535             return null;
    536         }
    537 
    538         XmlResourceParser xml =
    539                 appInfo.loadXmlMetaData(mContext.getPackageManager(), META_DATA_APP_RESTRICTIONS);
    540         List<RestrictionEntry> restrictions = loadManifestRestrictions(packageName, xml);
    541 
    542         return restrictions;
    543     }
    544 
    545     private List<RestrictionEntry> loadManifestRestrictions(String packageName,
    546             XmlResourceParser xml) {
    547         Context appContext;
    548         try {
    549             appContext = mContext.createPackageContext(packageName, 0 /* flags */);
    550         } catch (NameNotFoundException nnfe) {
    551             return null;
    552         }
    553         ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>();
    554         RestrictionEntry restriction;
    555 
    556         try {
    557             int tagType = xml.next();
    558             while (tagType != XmlPullParser.END_DOCUMENT) {
    559                 if (tagType == XmlPullParser.START_TAG) {
    560                     if (xml.getName().equals(TAG_RESTRICTION)) {
    561                         AttributeSet attrSet = Xml.asAttributeSet(xml);
    562                         if (attrSet != null) {
    563                             TypedArray a = appContext.obtainStyledAttributes(attrSet,
    564                                     com.android.internal.R.styleable.RestrictionEntry);
    565                             restriction = loadRestriction(appContext, a);
    566                             if (restriction != null) {
    567                                 restrictions.add(restriction);
    568                             }
    569                         }
    570                     }
    571                 }
    572                 tagType = xml.next();
    573             }
    574         } catch (XmlPullParserException e) {
    575             Log.w(TAG, "Reading restriction metadata for " + packageName, e);
    576             return null;
    577         } catch (IOException e) {
    578             Log.w(TAG, "Reading restriction metadata for " + packageName, e);
    579             return null;
    580         }
    581 
    582         return restrictions;
    583     }
    584 
    585     private RestrictionEntry loadRestriction(Context appContext, TypedArray a) {
    586         String key = a.getString(R.styleable.RestrictionEntry_key);
    587         int restrictionType = a.getInt(
    588                 R.styleable.RestrictionEntry_restrictionType, -1);
    589         String title = a.getString(R.styleable.RestrictionEntry_title);
    590         String description = a.getString(R.styleable.RestrictionEntry_description);
    591         int entries = a.getResourceId(R.styleable.RestrictionEntry_entries, 0);
    592         int entryValues = a.getResourceId(R.styleable.RestrictionEntry_entryValues, 0);
    593 
    594         if (restrictionType == -1) {
    595             Log.w(TAG, "restrictionType cannot be omitted");
    596             return null;
    597         }
    598 
    599         if (key == null) {
    600             Log.w(TAG, "key cannot be omitted");
    601             return null;
    602         }
    603 
    604         RestrictionEntry restriction = new RestrictionEntry(restrictionType, key);
    605         restriction.setTitle(title);
    606         restriction.setDescription(description);
    607         if (entries != 0) {
    608             restriction.setChoiceEntries(appContext, entries);
    609         }
    610         if (entryValues != 0) {
    611             restriction.setChoiceValues(appContext, entryValues);
    612         }
    613         // Extract the default value based on the type
    614         switch (restrictionType) {
    615             case RestrictionEntry.TYPE_NULL: // hidden
    616             case RestrictionEntry.TYPE_STRING:
    617             case RestrictionEntry.TYPE_CHOICE:
    618                 restriction.setSelectedString(
    619                         a.getString(R.styleable.RestrictionEntry_defaultValue));
    620                 break;
    621             case RestrictionEntry.TYPE_INTEGER:
    622                 restriction.setIntValue(
    623                         a.getInt(R.styleable.RestrictionEntry_defaultValue, 0));
    624                 break;
    625             case RestrictionEntry.TYPE_MULTI_SELECT:
    626                 int resId = a.getResourceId(R.styleable.RestrictionEntry_defaultValue, 0);
    627                 if (resId != 0) {
    628                     restriction.setAllSelectedStrings(
    629                             appContext.getResources().getStringArray(resId));
    630                 }
    631                 break;
    632             case RestrictionEntry.TYPE_BOOLEAN:
    633                 restriction.setSelectedState(
    634                         a.getBoolean(R.styleable.RestrictionEntry_defaultValue, false));
    635                 break;
    636             default:
    637                 Log.w(TAG, "Unknown restriction type " + restrictionType);
    638         }
    639         return restriction;
    640     }
    641 }
    642