diff options
author | Amith Yamasani <yamasani@google.com> | 2014-07-18 15:19:22 -0700 |
---|---|---|
committer | Amith Yamasani <yamasani@google.com> | 2014-07-22 00:03:39 +0000 |
commit | 9c44933958bc03b93eac2452b26dd75567581b86 (patch) | |
tree | 4e00f58d12fb315453de38e4254f1428d3cd8dde | |
parent | 79bf7284b9e4467cab87fd80fc2638f73a26f873 (diff) | |
download | frameworks_base-9c44933958bc03b93eac2452b26dd75567581b86.zip frameworks_base-9c44933958bc03b93eac2452b26dd75567581b86.tar.gz frameworks_base-9c44933958bc03b93eac2452b26dd75567581b86.tar.bz2 |
API Review: Improvements to RestrictionsManager API
Use an activity intent for local approval instead of a type.
Use PeristableBundle instead of Bundle.
Pass requestId as an explicit argument in cases where it's required.
Bug: 16400892
Change-Id: Id882033f17c39aa9cd63a7eeb73bb7b51f98cf5b
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | api/current.txt | 12 | ||||
-rw-r--r-- | core/java/android/content/AbstractRestrictionsProvider.java | 12 | ||||
-rw-r--r-- | core/java/android/content/IRestrictionsManager.aidl | 8 | ||||
-rw-r--r-- | core/java/android/content/RestrictionsManager.java | 97 | ||||
-rw-r--r-- | core/java/android/os/PersistableBundle.aidl | 20 | ||||
-rw-r--r-- | services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java | 40 |
7 files changed, 143 insertions, 47 deletions
@@ -446,6 +446,7 @@ aidl_files := \ frameworks/base/core/java/android/os/DropBoxManager.aidl \ frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \ frameworks/base/core/java/android/os/ParcelUuid.aidl \ + frameworks/base/core/java/android/os/PersistableBundle.aidl \ frameworks/base/core/java/android/print/PrinterInfo.aidl \ frameworks/base/core/java/android/print/PageRange.aidl \ frameworks/base/core/java/android/print/PrintAttributes.aidl \ diff --git a/api/current.txt b/api/current.txt index 6e25c2a..e6efcea 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6660,7 +6660,7 @@ package android.content { public abstract class AbstractRestrictionsProvider extends android.content.BroadcastReceiver { ctor public AbstractRestrictionsProvider(); method public void onReceive(android.content.Context, android.content.Intent); - method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle); + method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, java.lang.String, android.os.PersistableBundle); } public abstract class AbstractThreadedSyncAdapter { @@ -8050,14 +8050,17 @@ package android.content { public class RestrictionsManager { method public android.os.Bundle getApplicationRestrictions(); + method public android.content.Intent getLocalApprovalIntent(); method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String); method public boolean hasRestrictionsProvider(); - method public void notifyPermissionResponse(java.lang.String, android.os.Bundle); - method public void requestPermission(java.lang.String, android.os.Bundle); - field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED"; + method public void notifyPermissionResponse(java.lang.String, android.os.PersistableBundle); + method public void requestPermission(java.lang.String, java.lang.String, android.os.PersistableBundle); + field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.content.action.PERMISSION_RESPONSE_RECEIVED"; + field public static final java.lang.String ACTION_REQUEST_LOCAL_APPROVAL = "android.content.action.REQUEST_LOCAL_APPROVAL"; field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.content.action.REQUEST_PERMISSION"; field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME"; field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE"; + field public static final java.lang.String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID"; field public static final java.lang.String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE"; field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE"; field public static final java.lang.String META_DATA_APP_RESTRICTIONS = "android.content.APP_RESTRICTIONS"; @@ -8070,7 +8073,6 @@ package android.content { field public static final java.lang.String REQUEST_KEY_NEW_REQUEST = "android.request.new_request"; field public static final java.lang.String REQUEST_KEY_TITLE = "android.request.title"; field public static final java.lang.String REQUEST_TYPE_APPROVAL = "android.request.type.approval"; - field public static final java.lang.String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval"; field public static final java.lang.String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode"; field public static final java.lang.String RESPONSE_KEY_MESSAGE = "android.response.msg"; field public static final java.lang.String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp"; diff --git a/core/java/android/content/AbstractRestrictionsProvider.java b/core/java/android/content/AbstractRestrictionsProvider.java index 3272970..2b40870 100644 --- a/core/java/android/content/AbstractRestrictionsProvider.java +++ b/core/java/android/content/AbstractRestrictionsProvider.java @@ -17,8 +17,8 @@ package android.content; import android.app.admin.DevicePolicyManager; -import android.os.Bundle; import android.os.IBinder; +import android.os.PersistableBundle; /** * Abstract implementation of a Restrictions Provider BroadcastReceiver. To implement a @@ -30,7 +30,7 @@ import android.os.IBinder; * 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)}. + * {@link RestrictionsManager#notifyPermissionResponse(String, PersistableBundle)}. * * @see RestrictionsManager */ @@ -59,7 +59,7 @@ public abstract class AbstractRestrictionsProvider extends BroadcastReceiver { * @see RestrictionsManager#REQUEST_KEY_ID */ public abstract void requestPermission(Context context, - String packageName, String requestType, Bundle request); + String packageName, String requestType, String requestId, PersistableBundle request); /** * Intercept standard Restrictions Provider broadcasts. Implementations @@ -73,8 +73,10 @@ public abstract class AbstractRestrictionsProvider extends BroadcastReceiver { if (RestrictionsManager.ACTION_REQUEST_PERMISSION.equals(action)) { String packageName = intent.getStringExtra(RestrictionsManager.EXTRA_PACKAGE_NAME); String requestType = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_TYPE); - Bundle request = intent.getBundleExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE); - requestPermission(context, packageName, requestType, request); + String requestId = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_ID); + PersistableBundle request = (PersistableBundle) + intent.getParcelableExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE); + requestPermission(context, packageName, requestType, requestId, request); } } } diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl index b1c0a3a..495ac2e 100644 --- a/core/java/android/content/IRestrictionsManager.aidl +++ b/core/java/android/content/IRestrictionsManager.aidl @@ -16,7 +16,9 @@ package android.content; +import android.content.Intent; import android.os.Bundle; +import android.os.PersistableBundle; /** * Interface used by the RestrictionsManager @@ -25,6 +27,8 @@ import android.os.Bundle; interface IRestrictionsManager { Bundle getApplicationRestrictions(in String packageName); boolean hasRestrictionsProvider(); - void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData); - void notifyPermissionResponse(in String packageName, in Bundle response); + void requestPermission(in String packageName, in String requestType, in String requestId, + in PersistableBundle requestData); + void notifyPermissionResponse(in String packageName, in PersistableBundle response); + Intent getLocalApprovalIntent(); } diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java index 5ae10cf..c1226c0 100644 --- a/core/java/android/content/RestrictionsManager.java +++ b/core/java/android/content/RestrictionsManager.java @@ -16,6 +16,7 @@ package android.content; +import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -23,6 +24,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Bundle; +import android.os.PersistableBundle; import android.os.RemoteException; import android.util.AttributeSet; import android.util.Log; @@ -51,7 +53,7 @@ import java.util.List; * <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 + * {@link #notifyPermissionResponse(String, PersistableBundle)}, when * 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}. @@ -126,14 +128,15 @@ public class RestrictionsManager { * {@link #EXTRA_RESPONSE_BUNDLE}. */ public static final String ACTION_PERMISSION_RESPONSE_RECEIVED = - "android.intent.action.PERMISSION_RESPONSE_RECEIVED"; + "android.content.action.PERMISSION_RESPONSE_RECEIVED"; /** * Broadcast intent sent to the Restrictions Provider to handle a permission request from * an app. It will have the following extras: {@link #EXTRA_PACKAGE_NAME}, - * {@link #EXTRA_REQUEST_TYPE} and {@link #EXTRA_REQUEST_BUNDLE}. The Restrictions Provider - * will handle the request and respond back to the RestrictionsManager, when a response is - * available, by calling {@link #notifyPermissionResponse}. + * {@link #EXTRA_REQUEST_TYPE}, {@link #EXTRA_REQUEST_ID} and {@link #EXTRA_REQUEST_BUNDLE}. + * The Restrictions Provider will handle the request and respond back to the + * RestrictionsManager, when a response is available, by calling + * {@link #notifyPermissionResponse}. * <p> * The BroadcastReceiver must require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN} * permission to ensure that only the system can send the broadcast. @@ -142,17 +145,45 @@ public class RestrictionsManager { "android.content.action.REQUEST_PERMISSION"; /** + * Activity intent that is optionally implemented by the Restrictions Provider package + * to challenge for an administrator PIN or password locally on the device. Apps will + * call this intent using {@link Activity#startActivityForResult}. On a successful + * response, {@link Activity#onActivityResult} will return a resultCode of + * {@link Activity#RESULT_OK}. + * <p> + * The intent must contain {@link #EXTRA_REQUEST_BUNDLE} as an extra and the bundle must + * contain at least {@link #REQUEST_KEY_MESSAGE} for the activity to display. + * <p> + * @see #getLocalApprovalIntent() + */ + public static final String ACTION_REQUEST_LOCAL_APPROVAL = + "android.content.action.REQUEST_LOCAL_APPROVAL"; + + /** * The package name of the application making the request. + * <p> + * Type: String */ public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME"; /** * The request type passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast. + * <p> + * Type: String */ public static final String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE"; /** + * The request ID passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast. + * <p> + * Type: String + */ + public static final String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID"; + + /** * The request bundle passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast. + * <p> + * Type: {@link PersistableBundle} */ public static final String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE"; @@ -163,14 +194,15 @@ public class RestrictionsManager { * <li>{@link #REQUEST_KEY_ID}: The request ID.</li> * <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li> * </ul> + * <p> + * Type: {@link PersistableBundle} */ public static final String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE"; /** * 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}. + * Required keys are: {@link #REQUEST_KEY_MESSAGE} * <p> * Optional keys are * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE}, @@ -179,22 +211,6 @@ public class RestrictionsManager { public static final String REQUEST_TYPE_APPROVAL = "android.request.type.approval"; /** - * 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}. - * <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_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 @@ -240,9 +256,10 @@ public class RestrictionsManager { * Key for request icon contained in the request bundle. * <p> * Optional, shown alongside the request message presented to the administrator - * who approves the request. + * who approves the request. The content must be a compressed image such as a + * PNG or JPEG, as a byte array. * <p> - * Type: Bitmap + * Type: byte[] */ public static final String REQUEST_KEY_ICON = "android.request.icon"; @@ -403,7 +420,7 @@ public class RestrictionsManager { /** * Called by an application to check if there is an active Restrictions Provider. If - * there isn't, {@link #requestPermission(String, Bundle)} is not available. + * there isn't, {@link #requestPermission(String, String, PersistableBundle)} is not available. * * @return whether there is an active Restrictions Provider. */ @@ -428,40 +445,54 @@ public class RestrictionsManager { * 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 + * @param requestId A unique id generated by the app that contains sufficient information + * to identify the parameters of the request when it receives the id in the response. + * @param request A PersistableBundle containing the data corresponding to the specified request * type. The keys for the data in the bundle depend on the request type. * * @throws IllegalArgumentException if any of the required parameters are missing. */ - public void requestPermission(String requestType, Bundle request) { + public void requestPermission(String requestType, String requestId, PersistableBundle request) { if (requestType == null) { throw new NullPointerException("requestType cannot be null"); } + if (requestId == null) { + throw new NullPointerException("requestId cannot be null"); + } if (request == null) { throw new NullPointerException("request cannot be null"); } - if (!request.containsKey(REQUEST_KEY_ID)) { - throw new IllegalArgumentException("REQUEST_KEY_ID must be specified"); + try { + if (mService != null) { + mService.requestPermission(mContext.getPackageName(), requestType, requestId, + request); + } + } catch (RemoteException re) { + Log.w(TAG, "Couldn't reach service"); } + } + + public Intent getLocalApprovalIntent() { try { if (mService != null) { - mService.requestPermission(mContext.getPackageName(), requestType, request); + return mService.getLocalApprovalIntent(); } } catch (RemoteException re) { Log.w(TAG, "Couldn't reach service"); } + return null; } /** * Called by the Restrictions Provider to deliver a response to an application. * * @param packageName the application to deliver the response to. Cannot be null. - * @param response the Bundle containing the response status, request ID and other information. + * @param response the bundle containing the response status, request ID and other information. * Cannot be null. * * @throws IllegalArgumentException if any of the required parameters are missing. */ - public void notifyPermissionResponse(String packageName, Bundle response) { + public void notifyPermissionResponse(String packageName, PersistableBundle response) { if (packageName == null) { throw new NullPointerException("packageName cannot be null"); } diff --git a/core/java/android/os/PersistableBundle.aidl b/core/java/android/os/PersistableBundle.aidl new file mode 100644 index 0000000..5b05873 --- /dev/null +++ b/core/java/android/os/PersistableBundle.aidl @@ -0,0 +1,20 @@ +/* +** +** 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.os; + +parcelable PersistableBundle; diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java index 4fe30e6..fb29b6a 100644 --- a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java +++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java @@ -40,6 +40,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.IUserManager; +import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -103,7 +104,8 @@ public final class RestrictionsManagerService extends SystemService { @Override public void requestPermission(final String packageName, final String requestType, - final Bundle requestData) throws RemoteException { + final String requestId, + final PersistableBundle requestData) throws RemoteException { if (DEBUG) { Log.i(LOG_TAG, "requestPermission"); } @@ -127,6 +129,7 @@ public final class RestrictionsManagerService extends SystemService { intent.setComponent(restrictionsProvider); intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName); intent.putExtra(RestrictionsManager.EXTRA_REQUEST_TYPE, requestType); + intent.putExtra(RestrictionsManager.EXTRA_REQUEST_ID, requestId); intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData); mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle)); } finally { @@ -136,7 +139,40 @@ public final class RestrictionsManagerService extends SystemService { } @Override - public void notifyPermissionResponse(String packageName, Bundle response) + public Intent getLocalApprovalIntent() throws RemoteException { + if (DEBUG) { + Log.i(LOG_TAG, "requestPermission"); + } + final int userHandle = UserHandle.getCallingUserId(); + if (mDpm != null) { + long ident = Binder.clearCallingIdentity(); + try { + ComponentName restrictionsProvider = + mDpm.getRestrictionsProvider(userHandle); + // Check if there is a restrictions provider + if (restrictionsProvider == null) { + throw new IllegalStateException( + "Cannot request permission without a restrictions provider registered"); + } + String providerPackageName = restrictionsProvider.getPackageName(); + Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_LOCAL_APPROVAL); + intent.setPackage(providerPackageName); + ResolveInfo ri = AppGlobals.getPackageManager().resolveIntent(intent, + null /* resolvedType */, 0 /* flags */, userHandle); + if (ri != null && ri.activityInfo != null && ri.activityInfo.exported) { + intent.setComponent(new ComponentName(ri.activityInfo.packageName, + ri.activityInfo.name)); + return intent; + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + return null; + } + + @Override + public void notifyPermissionResponse(String packageName, PersistableBundle response) throws RemoteException { // Check caller int callingUid = Binder.getCallingUid(); |