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
This commit is contained in:
@@ -118,7 +118,9 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/content/IIntentReceiver.aidl \
|
||||
core/java/android/content/IIntentSender.aidl \
|
||||
core/java/android/content/IOnPrimaryClipChangedListener.aidl \
|
||||
core/java/android/content/IPermissionResponseCallback.aidl \
|
||||
core/java/android/content/IRestrictionsManager.aidl \
|
||||
core/java/android/content/IRestrictionsProvider.aidl \
|
||||
core/java/android/content/ISyncAdapter.aidl \
|
||||
core/java/android/content/ISyncContext.aidl \
|
||||
core/java/android/content/ISyncServiceAdapter.aidl \
|
||||
|
||||
@@ -6570,6 +6570,13 @@ package android.bluetooth.le {
|
||||
|
||||
package android.content {
|
||||
|
||||
public abstract class AbstractRestrictionsProvider extends android.app.Service {
|
||||
ctor public AbstractRestrictionsProvider();
|
||||
method public abstract android.os.Bundle getPermissionResponse(java.lang.String, java.lang.String);
|
||||
method public final android.os.IBinder onBind(android.content.Intent);
|
||||
method public abstract void requestPermission(java.lang.String, java.lang.String, android.os.Bundle);
|
||||
}
|
||||
|
||||
public abstract class AbstractThreadedSyncAdapter {
|
||||
ctor public AbstractThreadedSyncAdapter(android.content.Context, boolean);
|
||||
ctor public AbstractThreadedSyncAdapter(android.content.Context, boolean, boolean);
|
||||
@@ -7949,26 +7956,41 @@ package android.content {
|
||||
public class RestrictionsManager {
|
||||
method public android.os.Bundle getApplicationRestrictions();
|
||||
method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
|
||||
method public void getPermissionResponse(java.lang.String, android.content.RestrictionsManager.PermissionResponseCallback);
|
||||
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";
|
||||
field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.intent.action.REQUEST_PERMISSION";
|
||||
field public static final java.lang.String EXTRA_PACKAGE_NAME = "package_name";
|
||||
field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "request_bundle";
|
||||
field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "response_bundle";
|
||||
field public static final java.lang.String EXTRA_TEMPLATE_ID = "template_id";
|
||||
field public static final java.lang.String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept";
|
||||
field public static final java.lang.String REQUEST_KEY_DATA = "android.req_template.data";
|
||||
field public static final java.lang.String REQUEST_KEY_DENY_LABEL = "android.req_template.reject";
|
||||
field public static final java.lang.String REQUEST_KEY_DEVICE_NAME = "android.req_template.device";
|
||||
field public static final java.lang.String REQUEST_KEY_ICON = "android.req_template.icon";
|
||||
field public static final java.lang.String REQUEST_KEY_ID = "android.req_template.req_id";
|
||||
field public static final java.lang.String REQUEST_KEY_MESSAGE = "android.req_template.mesg";
|
||||
field public static final java.lang.String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor";
|
||||
field public static final java.lang.String REQUEST_KEY_TITLE = "android.req_template.title";
|
||||
field public static final java.lang.String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple";
|
||||
field public static final java.lang.String RESPONSE_KEY_BOOLEAN = "android.req_template.response";
|
||||
field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "response";
|
||||
field public static final java.lang.String REQUEST_KEY_APPROVE_LABEL = "android.request.approve_label";
|
||||
field public static final java.lang.String REQUEST_KEY_DATA = "android.request.data";
|
||||
field public static final java.lang.String REQUEST_KEY_DENY_LABEL = "android.request.deny_label";
|
||||
field public static final java.lang.String REQUEST_KEY_DEVICE_NAME = "android.request.device";
|
||||
field public static final java.lang.String REQUEST_KEY_ICON = "android.request.icon";
|
||||
field public static final java.lang.String REQUEST_KEY_ID = "android.request.id";
|
||||
field public static final java.lang.String REQUEST_KEY_MESSAGE = "android.request.mesg";
|
||||
field public static final java.lang.String REQUEST_KEY_REQUESTOR_NAME = "android.request.requestor";
|
||||
field public static final java.lang.String REQUEST_KEY_TITLE = "android.request.title";
|
||||
field public static final java.lang.String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval";
|
||||
field public static final java.lang.String REQUEST_TYPE_QUESTION = "android.request.type.question";
|
||||
field public static final java.lang.String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
|
||||
field public static final java.lang.String RESPONSE_KEY_ERROR_MESSAGE = "android.response.errormsg";
|
||||
field public static final java.lang.String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
|
||||
field public static final java.lang.String RESPONSE_KEY_RESULT = "android.response.result";
|
||||
field public static final int RESULT_APPROVED = 1; // 0x1
|
||||
field public static final int RESULT_DENIED = 2; // 0x2
|
||||
field public static final int RESULT_ERROR = 5; // 0x5
|
||||
field public static final int RESULT_ERROR_BAD_REQUEST = 1; // 0x1
|
||||
field public static final int RESULT_ERROR_INTERNAL = 3; // 0x3
|
||||
field public static final int RESULT_ERROR_NETWORK = 2; // 0x2
|
||||
field public static final int RESULT_NO_RESPONSE = 3; // 0x3
|
||||
field public static final int RESULT_UNKNOWN_REQUEST = 4; // 0x4
|
||||
}
|
||||
|
||||
public static abstract class RestrictionsManager.PermissionResponseCallback {
|
||||
ctor public RestrictionsManager.PermissionResponseCallback();
|
||||
method public abstract void onResponse(android.os.Bundle);
|
||||
}
|
||||
|
||||
public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.app.admin;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.app.Activity;
|
||||
import android.content.AbstractRestrictionsProvider;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -2661,19 +2662,19 @@ public class DevicePolicyManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Designates a specific broadcast receiver component as the provider for
|
||||
* Designates a specific service component as the provider for
|
||||
* making permission requests of a local or remote administrator of the user.
|
||||
* <p/>
|
||||
* Only a profile owner can designate the restrictions provider.
|
||||
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
|
||||
* @param receiver The component name of a BroadcastReceiver that handles the
|
||||
* {@link RestrictionsManager#ACTION_REQUEST_PERMISSION} intent. If this param is null,
|
||||
* @param provider The component name of the service that implements
|
||||
* {@link AbstractRestrictionsProvider}. If this param is null,
|
||||
* it removes the restrictions provider previously assigned.
|
||||
*/
|
||||
public void setRestrictionsProvider(ComponentName admin, ComponentName receiver) {
|
||||
public void setRestrictionsProvider(ComponentName admin, ComponentName provider) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.setRestrictionsProvider(admin, receiver);
|
||||
mService.setRestrictionsProvider(admin, provider);
|
||||
} catch (RemoteException re) {
|
||||
Log.w(TAG, "Failed to set permission provider on device policy service");
|
||||
}
|
||||
|
||||
93
core/java/android/content/AbstractRestrictionsProvider.java
Normal file
93
core/java/android/content/AbstractRestrictionsProvider.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
core/java/android/content/IPermissionResponseCallback.aidl
Normal file
30
core/java/android/content/IPermissionResponseCallback.aidl
Normal file
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
31
core/java/android/content/IRestrictionsProvider.aidl
Normal file
31
core/java/android/content/IRestrictionsProvider.aidl
Normal file
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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}.
|
||||
@@ -83,60 +88,23 @@ public class RestrictionsManager {
|
||||
public static final String ACTION_PERMISSION_RESPONSE_RECEIVED =
|
||||
"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 template that presents a simple question, with a possible title and icon.
|
||||
* 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}.
|
||||
@@ -145,17 +113,33 @@ 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_QUESTION = "android.request.type.question";
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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,21 +219,111 @@ 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
|
||||
*/
|
||||
@@ -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.
|
||||
* @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 requestTemplate, Bundle requestData) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,18 +17,23 @@
|
||||
package com.android.server.restrictions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.IAccountAuthenticator;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.IDevicePolicyManager;
|
||||
import android.content.AbstractRestrictionsProvider;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.IPermissionResponseCallback;
|
||||
import android.content.IRestrictionsProvider;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IRestrictionsManager;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
@@ -37,11 +42,13 @@ import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IUserManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.server.SystemService;
|
||||
@@ -50,8 +57,11 @@ import com.android.server.SystemService;
|
||||
* SystemService wrapper for the RestrictionsManager implementation. Publishes the
|
||||
* Context.RESTRICTIONS_SERVICE.
|
||||
*/
|
||||
|
||||
public final class RestrictionsManagerService extends SystemService {
|
||||
|
||||
static final String LOG_TAG = "RestrictionsManagerService";
|
||||
static final boolean DEBUG = false;
|
||||
|
||||
private final RestrictionsManagerImpl mRestrictionsManagerImpl;
|
||||
|
||||
public RestrictionsManagerService(Context context) {
|
||||
@@ -65,7 +75,7 @@ public final class RestrictionsManagerService extends SystemService {
|
||||
}
|
||||
|
||||
class RestrictionsManagerImpl extends IRestrictionsManager.Stub {
|
||||
private final Context mContext;
|
||||
final Context mContext;
|
||||
private final IUserManager mUm;
|
||||
private final IDevicePolicyManager mDpm;
|
||||
|
||||
@@ -96,8 +106,11 @@ public final class RestrictionsManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPermission(String packageName, String requestTemplate,
|
||||
Bundle requestData) throws RemoteException {
|
||||
public void requestPermission(final String packageName, final String requestType,
|
||||
final Bundle requestData) throws RemoteException {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "requestPermission");
|
||||
}
|
||||
int callingUid = Binder.getCallingUid();
|
||||
int userHandle = UserHandle.getUserId(callingUid);
|
||||
if (mDpm != null) {
|
||||
@@ -114,29 +127,61 @@ public final class RestrictionsManagerService extends SystemService {
|
||||
enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
|
||||
" match caller ");
|
||||
// Prepare and broadcast the intent to the provider
|
||||
Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(restrictionsProvider);
|
||||
intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
|
||||
intent.putExtra(RestrictionsManager.EXTRA_TEMPLATE_ID, requestTemplate);
|
||||
intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
|
||||
mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
|
||||
new ProviderServiceConnection(intent, null, userHandle) {
|
||||
@Override
|
||||
public void run() throws RemoteException {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "calling requestPermission for " + packageName
|
||||
+ ", type=" + requestType + ", data=" + requestData);
|
||||
}
|
||||
mRestrictionsProvider.requestPermission(packageName,
|
||||
requestType, requestData);
|
||||
}
|
||||
}.bind();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceCallerMatchesPackage(int callingUid, String packageName,
|
||||
String message) {
|
||||
try {
|
||||
String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid);
|
||||
if (pkgs != null) {
|
||||
if (!ArrayUtils.contains(pkgs, packageName)) {
|
||||
throw new SecurityException(message + callingUid);
|
||||
@Override
|
||||
public void getPermissionResponse(final String packageName, final String requestId,
|
||||
final IPermissionResponseCallback callback) throws RemoteException {
|
||||
int callingUid = Binder.getCallingUid();
|
||||
int userHandle = UserHandle.getUserId(callingUid);
|
||||
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 fetch permission without a restrictions provider registered");
|
||||
}
|
||||
// Check that the packageName matches the caller.
|
||||
enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
|
||||
" match caller ");
|
||||
// Prepare and broadcast the intent to the provider
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(restrictionsProvider);
|
||||
new ProviderServiceConnection(intent, callback.asBinder(), userHandle) {
|
||||
@Override
|
||||
public void run() throws RemoteException {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "calling getPermissionResponse for " + packageName
|
||||
+ ", id=" + requestId);
|
||||
}
|
||||
Bundle response = mRestrictionsProvider.getPermissionResponse(
|
||||
packageName, requestId);
|
||||
callback.onResponse(response);
|
||||
}
|
||||
}.bind();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
// Shouldn't happen
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,5 +212,95 @@ public final class RestrictionsManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceCallerMatchesPackage(int callingUid, String packageName,
|
||||
String message) {
|
||||
try {
|
||||
String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid);
|
||||
if (pkgs != null) {
|
||||
if (!ArrayUtils.contains(pkgs, packageName)) {
|
||||
throw new SecurityException(message + callingUid);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
// Shouldn't happen
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ProviderServiceConnection
|
||||
implements IBinder.DeathRecipient, ServiceConnection {
|
||||
|
||||
protected IRestrictionsProvider mRestrictionsProvider;
|
||||
private Intent mIntent;
|
||||
protected int mUserHandle;
|
||||
protected IBinder mResponse;
|
||||
private boolean mAbort;
|
||||
|
||||
public ProviderServiceConnection(Intent intent, IBinder response, int userHandle) {
|
||||
mIntent = intent;
|
||||
mResponse = response;
|
||||
mUserHandle = userHandle;
|
||||
if (mResponse != null) {
|
||||
try {
|
||||
mResponse.linkToDeath(this, 0 /* flags */);
|
||||
} catch (RemoteException re) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Bind to the RestrictionsProvider process */
|
||||
public void bind() {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "binding to service: " + mIntent);
|
||||
}
|
||||
mContext.bindServiceAsUser(mIntent, this, Context.BIND_AUTO_CREATE,
|
||||
new UserHandle(mUserHandle));
|
||||
}
|
||||
|
||||
private void close() {
|
||||
mAbort = true;
|
||||
unbind();
|
||||
}
|
||||
|
||||
private void unbind() {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "unbinding from service");
|
||||
}
|
||||
mContext.unbindService(this);
|
||||
}
|
||||
|
||||
/** Implement this to call the appropriate method on the service */
|
||||
public abstract void run() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "connected to " + name);
|
||||
}
|
||||
mRestrictionsProvider = IRestrictionsProvider.Stub.asInterface(service);
|
||||
if (!mAbort) {
|
||||
try {
|
||||
run();
|
||||
} catch (RemoteException re) {
|
||||
Log.w("RestrictionsProvider", "Remote exception: " + re);
|
||||
}
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "disconnected from " + name);
|
||||
}
|
||||
mRestrictionsProvider = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
mAbort = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user