diff options
author | Amith Yamasani <yamasani@google.com> | 2014-07-10 13:41:55 -0700 |
---|---|---|
committer | Amith Yamasani <yamasani@google.com> | 2014-07-13 07:17:56 -0700 |
commit | f6e2fcc2eaf5e650237d3c6d79a2de49d2d4111e (patch) | |
tree | fc4d32ee496c1021901cb5b50e463459908d7ae7 /core/java/android/content | |
parent | 7ab9d759f5b5238b4ee6013f57454ed5d5f4927f (diff) | |
download | frameworks_base-f6e2fcc2eaf5e650237d3c6d79a2de49d2d4111e.zip frameworks_base-f6e2fcc2eaf5e650237d3c6d79a2de49d2d4111e.tar.gz frameworks_base-f6e2fcc2eaf5e650237d3c6d79a2de49d2d4111e.tar.bz2 |
Improvements to Restrictions API
Convert restrictions provider to a service instead of a receiver.
Add a way to get pending responses from restrictions provider.
Add AbstractRestrictionsProvider.
Add a callback API for responses.
Removed some constants in RestrictionsManager.
Added new constants for errors and error codes.
Much improved javadocs.
Bug: 16176009
Change-Id: I838a50fabc80b94b632294b3a55cd5d8092acf55
Diffstat (limited to 'core/java/android/content')
5 files changed, 361 insertions, 83 deletions
diff --git a/core/java/android/content/AbstractRestrictionsProvider.java b/core/java/android/content/AbstractRestrictionsProvider.java new file mode 100644 index 0000000..1119478 --- /dev/null +++ b/core/java/android/content/AbstractRestrictionsProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.app.Service; +import android.app.admin.DevicePolicyManager; +import android.os.Bundle; +import android.os.IBinder; + +/** + * Abstract implementation of a Restrictions Provider Service. To implement a Restrictions Provider, + * extend from this class and implement the abstract methods. Export this service in the + * manifest. A profile owner device admin can then register this component as a Restrictions + * Provider using {@link DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)}. + * <p> + * The function of a Restrictions Provider is to transport permission requests from apps on this + * device to an administrator (most likely on a remote device or computer) and deliver back + * responses. The response should be sent back to the app via + * {@link RestrictionsManager#notifyPermissionResponse(String, Bundle)}. + * <p> + * Apps can also query previously received responses using + * {@link #getPermissionResponse(String, String)}. The period for which previously received + * responses are available is left to the implementation of the Restrictions Provider. + */ +public abstract class AbstractRestrictionsProvider extends Service { + + private static final String TAG = "AbstractRestrictionsProvider"; + + @Override + public final IBinder onBind(Intent intent) { + return new RestrictionsProviderWrapper().asBinder(); + } + + /** + * Checks to see if there is a response for a prior request and returns the response bundle if + * it exists. If there is no response yet or if the request is not known, the returned bundle + * should contain the response code in {@link RestrictionsManager#RESPONSE_KEY_RESULT}. + * + * @param packageName the application that is requesting a permission response. + * @param requestId the id of the request for which the response is needed. + * @return a bundle containing at a minimum the result of the request. It could contain other + * optional information such as error codes and cookies. + * + * @see RestrictionsManager#RESPONSE_KEY_RESULT + */ + public abstract Bundle getPermissionResponse(String packageName, String requestId); + + /** + * An asynchronous permission request made by an application for an operation that requires + * authorization by a local or remote administrator other than the user. The Restrictions + * Provider must transfer the request to the administrator and deliver back a response, when + * available. The calling application is aware that the response could take an indefinite + * amount of time. + * + * @param packageName the application requesting permission. + * @param requestType the type of request, which determines the content and presentation of + * the request data. + * @param request the request data bundle containing at a minimum a request id. + * + * @see RestrictionsManager#REQUEST_TYPE_QUESTION + * @see RestrictionsManager#REQUEST_TYPE_LOCAL_APPROVAL + * @see RestrictionsManager#REQUEST_KEY_ID + */ + public abstract void requestPermission(String packageName, String requestType, Bundle request); + + private class RestrictionsProviderWrapper extends IRestrictionsProvider.Stub { + + @Override + public Bundle getPermissionResponse(String packageName, String requestId) { + return AbstractRestrictionsProvider.this + .getPermissionResponse(packageName, requestId); + } + + @Override + public void requestPermission(String packageName, String templateId, Bundle request) { + AbstractRestrictionsProvider.this.requestPermission(packageName, templateId, request); + } + } +} diff --git a/core/java/android/content/IPermissionResponseCallback.aidl b/core/java/android/content/IPermissionResponseCallback.aidl new file mode 100644 index 0000000..8309768 --- /dev/null +++ b/core/java/android/content/IPermissionResponseCallback.aidl @@ -0,0 +1,30 @@ +/* +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content; + +import android.os.Bundle; + +/** + * Callback for permission response queries. + * + * @hide + */ + interface IPermissionResponseCallback { + + void onResponse(in Bundle response); + +} diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl index b1c0a3a..49eb65b 100644 --- a/core/java/android/content/IRestrictionsManager.aidl +++ b/core/java/android/content/IRestrictionsManager.aidl @@ -17,6 +17,7 @@ package android.content; import android.os.Bundle; +import android.content.IPermissionResponseCallback; /** * Interface used by the RestrictionsManager @@ -27,4 +28,6 @@ interface IRestrictionsManager { boolean hasRestrictionsProvider(); void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData); void notifyPermissionResponse(in String packageName, in Bundle response); + void getPermissionResponse(in String packageName, in String requestId, + in IPermissionResponseCallback callback); } diff --git a/core/java/android/content/IRestrictionsProvider.aidl b/core/java/android/content/IRestrictionsProvider.aidl new file mode 100644 index 0000000..4506b72 --- /dev/null +++ b/core/java/android/content/IRestrictionsProvider.aidl @@ -0,0 +1,31 @@ +/* +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content; + +import android.os.Bundle; + +/** + * Interface to a restrictions provider service component. + * + * @hide + */ + interface IRestrictionsProvider { + + void requestPermission(in String packageName, in String requestType, in Bundle requestBundle); + Bundle getPermissionResponse(in String packageName, in String requestId); + +} diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java index 0dd0edd..5ef2dbc 100644 --- a/core/java/android/content/RestrictionsManager.java +++ b/core/java/android/content/RestrictionsManager.java @@ -30,23 +30,26 @@ import java.util.List; * device administrator to override default app-specific restrictions or any other * operation that needs explicit authorization from the administrator. * <p> - * Apps can expose a set of restrictions via a runtime receiver mechanism or via - * static meta data in the manifest. + * Apps can expose a set of restrictions via an XML file specified in the manifest. * <p> * If the user has an active restrictions provider, dynamic requests can be made in * addition to the statically imposed restrictions. Dynamic requests are app-specific - * and can be expressed via a predefined set of templates. + * and can be expressed via a predefined set of request types. * <p> * The RestrictionsManager forwards the dynamic requests to the active * restrictions provider. The restrictions provider can respond back to requests by calling * {@link #notifyPermissionResponse(String, Bundle)}, when - * a response is received from the administrator of the device or user + * a response is received from the administrator of the device or user. * The response is relayed back to the application via a protected broadcast, * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}. * <p> + * Prior responses to requests can also be queried through calls to + * {@link #getPermissionResponse(String, PermissionResponseCallback)}, if the provider + * saves old responses. + * <p> * Static restrictions are specified by an XML file referenced by a meta-data attribute * in the manifest. This enables applications as well as any web administration consoles - * to be able to read the template from the apk. + * to be able to read the list of available restrictions from the apk. * <p> * The syntax of the XML format is as follows: * <pre> @@ -62,20 +65,22 @@ import java.util.List; * The attributes for each restriction depend on the restriction type. * * @see RestrictionEntry + * @see AbstractRestrictionsProvider */ public class RestrictionsManager { + private static final String TAG = "RestrictionsManager"; + /** * Broadcast intent delivered when a response is received for a permission * request. The response is not available for later query, so the receiver * must persist and/or immediately act upon the response. The application * should not interrupt the user by coming to the foreground if it isn't - * currently in the foreground. It can post a notification instead, informing - * the user of a change in state. + * currently in the foreground. It can either post a notification informing + * the user of the response or wait until the next time the user launches the app. * <p> * For instance, if the user requested permission to make an in-app purchase, - * the app can post a notification that the request had been granted or denied, - * and allow the purchase to go through. + * the app can post a notification that the request had been approved or denied. * <p> * The broadcast Intent carries the following extra: * {@link #EXTRA_RESPONSE_BUNDLE}. @@ -84,59 +89,38 @@ public class RestrictionsManager { "android.intent.action.PERMISSION_RESPONSE_RECEIVED"; /** - * Protected broadcast intent sent to the active restrictions provider. The intent - * contains the following extras:<p> - * <ul> - * <li>{@link #EXTRA_PACKAGE_NAME} : String; the package name of the application requesting - * permission.</li> - * <li>{@link #EXTRA_TEMPLATE_ID} : String; the template of the request.</li> - * <li>{@link #EXTRA_REQUEST_BUNDLE} : Bundle; contains the template-specific keys and values - * for the request. - * </ul> - * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName) - * @see #requestPermission(String, String, Bundle) - */ - public static final String ACTION_REQUEST_PERMISSION = - "android.intent.action.REQUEST_PERMISSION"; - - /** * The package name of the application making the request. */ public static final String EXTRA_PACKAGE_NAME = "package_name"; /** - * The template id that specifies what kind of a request it is and may indicate - * how the request is to be presented to the administrator. Must be either one of - * the predefined templates or a custom one specified by the application that the - * restrictions provider is familiar with. - */ - public static final String EXTRA_TEMPLATE_ID = "template_id"; - - /** - * A bundle containing the details about the request. The contents depend on the - * template id. - * @see #EXTRA_TEMPLATE_ID - */ - public static final String EXTRA_REQUEST_BUNDLE = "request_bundle"; - - /** * Contains a response from the administrator for specific request. * The bundle contains the following information, at least: * <ul> - * <li>{@link #REQUEST_KEY_ID}: The request id.</li> - * <li>{@link #REQUEST_KEY_DATA}: The request reference data.</li> + * <li>{@link #REQUEST_KEY_ID}: The request ID.</li> + * <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li> * </ul> - * <p> - * And depending on what the request template was, the bundle will contain the actual - * result of the request. For {@link #REQUEST_TEMPLATE_QUESTION}, the result will be in - * {@link #RESPONSE_KEY_BOOLEAN}, which is of type boolean; true if the administrator - * approved the request, false otherwise. */ - public static final String EXTRA_RESPONSE_BUNDLE = "response_bundle"; + public static final String EXTRA_RESPONSE_BUNDLE = "response"; + /** + * Request type for a simple question, with a possible title and icon. + * <p> + * Required keys are + * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}. + * <p> + * Optional keys are + * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE}, + * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}. + */ + public static final String REQUEST_TYPE_QUESTION = "android.request.type.question"; /** - * Request template that presents a simple question, with a possible title and icon. + * Request type for a local password challenge. This is a way for an app to ask + * the administrator to override an operation that is restricted on the device, such + * as configuring Wi-Fi access points. It is most useful for situations where there + * is no network connectivity for a remote administrator's response. The normal user of the + * device is not expected to know the password. The challenge is meant for the administrator. * <p> * Required keys are * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}. @@ -145,17 +129,17 @@ public class RestrictionsManager { * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE}, * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}. */ - public static final String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple"; + public static final String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval"; /** * Key for request ID contained in the request bundle. * <p> - * App-generated request id to identify the specific request when receiving + * App-generated request ID to identify the specific request when receiving * a response. This value is returned in the {@link #EXTRA_RESPONSE_BUNDLE}. * <p> * Type: String */ - public static final String REQUEST_KEY_ID = "android.req_template.req_id"; + public static final String REQUEST_KEY_ID = "android.request.id"; /** * Key for request data contained in the request bundle. @@ -167,7 +151,7 @@ public class RestrictionsManager { * <p> * Type: String */ - public static final String REQUEST_KEY_DATA = "android.req_template.data"; + public static final String REQUEST_KEY_DATA = "android.request.data"; /** * Key for request title contained in the request bundle. @@ -177,7 +161,7 @@ public class RestrictionsManager { * <p> * Type: String */ - public static final String REQUEST_KEY_TITLE = "android.req_template.title"; + public static final String REQUEST_KEY_TITLE = "android.request.title"; /** * Key for request message contained in the request bundle. @@ -187,7 +171,7 @@ public class RestrictionsManager { * <p> * Type: String */ - public static final String REQUEST_KEY_MESSAGE = "android.req_template.mesg"; + public static final String REQUEST_KEY_MESSAGE = "android.request.mesg"; /** * Key for request icon contained in the request bundle. @@ -197,7 +181,7 @@ public class RestrictionsManager { * <p> * Type: Bitmap */ - public static final String REQUEST_KEY_ICON = "android.req_template.icon"; + public static final String REQUEST_KEY_ICON = "android.request.icon"; /** * Key for request approval button label contained in the request bundle. @@ -207,7 +191,7 @@ public class RestrictionsManager { * <p> * Type: String */ - public static final String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept"; + public static final String REQUEST_KEY_APPROVE_LABEL = "android.request.approve_label"; /** * Key for request rejection button label contained in the request bundle. @@ -217,7 +201,7 @@ public class RestrictionsManager { * <p> * Type: String */ - public static final String REQUEST_KEY_DENY_LABEL = "android.req_template.reject"; + public static final String REQUEST_KEY_DENY_LABEL = "android.request.deny_label"; /** * Key for requestor's name contained in the request bundle. This value is not specified by @@ -226,7 +210,7 @@ public class RestrictionsManager { * <p> * Type: String */ - public static final String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor"; + public static final String REQUEST_KEY_REQUESTOR_NAME = "android.request.requestor"; /** * Key for requestor's device name contained in the request bundle. This value is not specified @@ -235,22 +219,112 @@ public class RestrictionsManager { * <p> * Type: String */ - public static final String REQUEST_KEY_DEVICE_NAME = "android.req_template.device"; + public static final String REQUEST_KEY_DEVICE_NAME = "android.request.device"; /** * Key for the response in the response bundle sent to the application, for a permission * request. * <p> - * Type: boolean + * Type: int + * <p> + * Possible values: {@link #RESULT_APPROVED}, {@link #RESULT_DENIED}, + * {@link #RESULT_NO_RESPONSE}, {@link #RESULT_UNKNOWN_REQUEST} or + * {@link #RESULT_ERROR}. */ - public static final String RESPONSE_KEY_BOOLEAN = "android.req_template.response"; + public static final String RESPONSE_KEY_RESULT = "android.response.result"; - private static final String TAG = "RestrictionsManager"; + /** + * Response result value indicating that the request was approved. + */ + public static final int RESULT_APPROVED = 1; + + /** + * Response result value indicating that the request was denied. + */ + public static final int RESULT_DENIED = 2; + + /** + * Response result value indicating that the request has not received a response yet. + */ + public static final int RESULT_NO_RESPONSE = 3; + + /** + * Response result value indicating that the request is unknown, when returned through a + * call to #getPendingResponse + */ + public static final int RESULT_UNKNOWN_REQUEST = 4; + + /** + * Response result value indicating an error condition. Additional error code might be available + * in the response bundle, for the key {@link #RESPONSE_KEY_ERROR_CODE}. There might also be + * an associated error message in the response bundle, for the key + * {@link #RESPONSE_KEY_ERROR_MESSAGE}. + */ + public static final int RESULT_ERROR = 5; + + /** + * Error code indicating that there was a problem with the request. + * <p> + * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle. + */ + public static final int RESULT_ERROR_BAD_REQUEST = 1; + + /** + * Error code indicating that there was a problem with the network. + * <p> + * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle. + */ + public static final int RESULT_ERROR_NETWORK = 2; + + /** + * Error code indicating that there was an internal error. + * <p> + * Stored in {@link #RESPONSE_KEY_ERROR_CODE} field in the response bundle. + */ + public static final int RESULT_ERROR_INTERNAL = 3; + + /** + * Key for the optional error code in the response bundle sent to the application. + * <p> + * Type: int + * <p> + * Possible values: {@link #RESULT_ERROR_BAD_REQUEST}, {@link #RESULT_ERROR_NETWORK} or + * {@link #RESULT_ERROR_INTERNAL}. + */ + public static final String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode"; + + /** + * Key for the optional error message in the response bundle sent to the application. + * <p> + * Type: String + */ + public static final String RESPONSE_KEY_ERROR_MESSAGE = "android.response.errormsg"; + + /** + * Key for the optional timestamp of when the administrator responded to the permission + * request. It is an represented in milliseconds since January 1, 1970 00:00:00.0 UTC. + * <p> + * Type: long + */ + public static final String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp"; private final Context mContext; private final IRestrictionsManager mService; /** + * Callback object for returning a response for a request. + * + * @see #getPermissionResponse + */ + public static abstract class PermissionResponseCallback { + /** + * Contains the response + * @param response + */ + public abstract void onResponse(Bundle response); + } + + /** * @hide */ public RestrictionsManager(Context context, IRestrictionsManager service) { @@ -261,7 +335,8 @@ public class RestrictionsManager { /** * Returns any available set of application-specific restrictions applicable * to this application. - * @return + * @return the application restrictions as a Bundle. Returns null if there + * are no restrictions. */ public Bundle getApplicationRestrictions() { try { @@ -275,10 +350,11 @@ public class RestrictionsManager { } /** - * Called by an application to check if permission requests can be made. If false, - * there is no need to request permission for an operation, unless a static - * restriction applies to that operation. - * @return + * Called by an application to check if there is an active restrictions provider. If + * there isn't, {@link #getPermissionResponse(String, PermissionResponseCallback)} + * and {@link #requestPermission(String, Bundle)} are not available. + * + * @return whether there is an active restrictions provider. */ public boolean hasRestrictionsProvider() { try { @@ -294,20 +370,20 @@ public class RestrictionsManager { /** * Called by an application to request permission for an operation. The contents of the * request are passed in a Bundle that contains several pieces of data depending on the - * chosen request template. + * chosen request type. * - * @param requestTemplate The request template to use. The template could be one of the - * predefined templates specified in this class or a custom template that the specific - * Restrictions Provider might understand. For custom templates, the template name should be - * namespaced to avoid collisions with predefined templates and templates specified by - * other Restrictions Provider vendors. - * @param requestData A Bundle containing the data corresponding to the specified request - * template. The keys for the data in the bundle depend on the kind of template chosen. - */ - public void requestPermission(String requestTemplate, Bundle requestData) { + * @param requestType The type of request. The type could be one of the + * predefined types specified here or a custom type that the specific + * restrictions provider might understand. For custom types, the type name should be + * namespaced to avoid collisions with predefined types and types specified by + * other restrictions providers. + * @param request A Bundle containing the data corresponding to the specified request + * type. The keys for the data in the bundle depend on the request type. + */ + public void requestPermission(String requestType, Bundle request) { try { if (mService != null) { - mService.requestPermission(mContext.getPackageName(), requestTemplate, requestData); + mService.requestPermission(mContext.getPackageName(), requestType, request); } } catch (RemoteException re) { Log.w(TAG, "Couldn't reach service"); @@ -315,10 +391,39 @@ public class RestrictionsManager { } /** - * Called by the Restrictions Provider when a response is available to be - * delivered to an application. - * @param packageName - * @param response + * Called by an application to query for any available response from the restrictions provider + * for the given requestId. The call returns immediately and the response will be returned + * via the provided callback. This does not initiate a new request and does not wait + * for a response to be received. It merely returns any previously received response + * or indicates if there was no available response. If there are multiple responses + * available for the same request ID, the most recent one is returned. + * + * @param requestId The ID of the original request made via + * {@link #requestPermission(String, Bundle)}. It's possible to also query for responses + * to requests made on a different device with the same requestId, if the Restrictions + * Provider happens to sync responses across devices with the same account managed by the + * restrictions provider. + * @param callback The response is returned via the callback object. Cannot be null. + */ + public void getPermissionResponse(String requestId, PermissionResponseCallback callback) { + if (requestId == null || callback == null) { + throw new NullPointerException("requestId or callback cannot be null"); + } + try { + if (mService != null) { + mService.getPermissionResponse(mContext.getPackageName(), requestId, + new PermissionResponseCallbackWrapper(callback)); + } + } catch (RemoteException re) { + Log.w(TAG, "Couldn't reach service"); + } + } + + /** + * Called by the restrictions provider to deliver a response to an application. + * + * @param packageName the application to deliver the response to. + * @param response the Bundle containing the response status, request ID and other information. */ public void notifyPermissionResponse(String packageName, Bundle response) { try { @@ -333,6 +438,7 @@ public class RestrictionsManager { /** * Parse and return the list of restrictions defined in the manifest for the specified * package, if any. + * * @param packageName The application for which to fetch the restrictions list. * @return The list of RestrictionEntry objects created from the XML file specified * in the manifest, or null if none was specified. @@ -341,4 +447,19 @@ public class RestrictionsManager { // TODO: return null; } + + private static class PermissionResponseCallbackWrapper + extends IPermissionResponseCallback.Stub { + + private PermissionResponseCallback mCallback; + + PermissionResponseCallbackWrapper(PermissionResponseCallback callback) { + mCallback = callback; + } + + @Override + public void onResponse(Bundle response) { + mCallback.onResponse(response); + } + } } |