• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
RestrictionsManager(Context context, IRestrictionsManager service)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      */
getApplicationRestrictions()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      */
hasRestrictionsProvider()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      */
requestPermission(String requestType, String requestId, PersistableBundle request)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 
createLocalApprovalIntent()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      */
notifyPermissionResponse(String packageName, PersistableBundle response)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      */
getManifestRestrictions(String packageName)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 
loadManifestRestrictions(String packageName, XmlResourceParser xml)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 
loadRestriction(Context appContext, TypedArray a)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