294 lines
11 KiB
Java
294 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2016 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.view.inputmethod;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.UserIdInt;
|
|
import android.content.ClipDescription;
|
|
import android.content.ContentProvider;
|
|
import android.net.Uri;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.os.RemoteException;
|
|
import android.os.UserHandle;
|
|
|
|
import com.android.internal.inputmethod.IInputContentUriToken;
|
|
|
|
import java.security.InvalidParameterException;
|
|
|
|
/**
|
|
* A container object with which input methods can send content files to the target application.
|
|
*/
|
|
public final class InputContentInfo implements Parcelable {
|
|
|
|
/**
|
|
* The content URI that may or may not have a user ID embedded by
|
|
* {@link ContentProvider#maybeAddUserId(Uri, int)}. This always preserves the exact value
|
|
* specified to a constructor. In other words, if it had user ID embedded when it was passed
|
|
* to the constructor, it still has the same user ID no matter if it is valid or not.
|
|
*/
|
|
@NonNull
|
|
private final Uri mContentUri;
|
|
/**
|
|
* The user ID to which {@link #mContentUri} belongs to. If {@link #mContentUri} already
|
|
* embedded the user ID when it was specified then this fields has the same user ID. Otherwise
|
|
* the user ID is determined based on the process ID when the constructor is called.
|
|
*
|
|
* <p>CAUTION: If you received {@link InputContentInfo} from a different process, there is no
|
|
* guarantee that this value is correct and valid. Never use this for any security purpose</p>
|
|
*/
|
|
@UserIdInt
|
|
private final int mContentUriOwnerUserId;
|
|
@NonNull
|
|
private final ClipDescription mDescription;
|
|
@Nullable
|
|
private final Uri mLinkUri;
|
|
@NonNull
|
|
private IInputContentUriToken mUriToken;
|
|
|
|
/**
|
|
* Constructs {@link InputContentInfo} object only with mandatory data.
|
|
*
|
|
* @param contentUri Content URI to be exported from the input method.
|
|
* This cannot be {@code null}.
|
|
* @param description A {@link ClipDescription} object that contains the metadata of
|
|
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
|
|
* {@link ClipDescription#getLabel()} should be describing the content specified by
|
|
* {@code contentUri} for accessibility reasons.
|
|
*/
|
|
public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) {
|
|
this(contentUri, description, null /* link Uri */);
|
|
}
|
|
|
|
/**
|
|
* Constructs {@link InputContentInfo} object with additional link URI.
|
|
*
|
|
* @param contentUri Content URI to be exported from the input method.
|
|
* This cannot be {@code null}.
|
|
* @param description A {@link ClipDescription} object that contains the metadata of
|
|
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
|
|
* {@link ClipDescription#getLabel()} should be describing the content specified by
|
|
* {@code contentUri} for accessibility reasons.
|
|
* @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
|
|
* a way to navigate the user to the specified web page if this is not {@code null}.
|
|
* @throws InvalidParameterException if any invalid parameter is specified.
|
|
*/
|
|
public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description,
|
|
@Nullable Uri linkUri) {
|
|
validateInternal(contentUri, description, linkUri, true /* throwException */);
|
|
mContentUri = contentUri;
|
|
mContentUriOwnerUserId =
|
|
ContentProvider.getUserIdFromUri(mContentUri, UserHandle.myUserId());
|
|
mDescription = description;
|
|
mLinkUri = linkUri;
|
|
}
|
|
|
|
/**
|
|
* @return {@code true} if all the fields are valid.
|
|
* @hide
|
|
*/
|
|
public boolean validate() {
|
|
return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */);
|
|
}
|
|
|
|
/**
|
|
* Constructs {@link InputContentInfo} object with additional link URI.
|
|
*
|
|
* @param contentUri Content URI to be exported from the input method.
|
|
* This cannot be {@code null}.
|
|
* @param description A {@link ClipDescription} object that contains the metadata of
|
|
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
|
|
* {@link ClipDescription#getLabel()} should be describing the content specified by
|
|
* {@code contentUri} for accessibility reasons.
|
|
* @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
|
|
* a way to navigate the user to the specified web page if this is not {@code null}.
|
|
* @param throwException {@code true} if this method should throw an
|
|
* {@link InvalidParameterException}.
|
|
* @throws InvalidParameterException if any invalid parameter is specified.
|
|
*/
|
|
private static boolean validateInternal(@NonNull Uri contentUri,
|
|
@NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) {
|
|
if (contentUri == null) {
|
|
if (throwException) {
|
|
throw new NullPointerException("contentUri");
|
|
}
|
|
return false;
|
|
}
|
|
if (description == null) {
|
|
if (throwException) {
|
|
throw new NullPointerException("description");
|
|
}
|
|
return false;
|
|
}
|
|
final String contentUriScheme = contentUri.getScheme();
|
|
if (!"content".equals(contentUriScheme)) {
|
|
if (throwException) {
|
|
throw new InvalidParameterException("contentUri must have content scheme");
|
|
}
|
|
return false;
|
|
}
|
|
if (linkUri != null) {
|
|
final String scheme = linkUri.getScheme();
|
|
if (scheme == null ||
|
|
(!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
|
|
if (throwException) {
|
|
throw new InvalidParameterException(
|
|
"linkUri must have either http or https scheme");
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return Content URI with which the content can be obtained.
|
|
*/
|
|
@NonNull
|
|
public Uri getContentUri() {
|
|
// Fix up the content URI when and only when the caller's user ID does not match the owner's
|
|
// user ID.
|
|
if (mContentUriOwnerUserId != UserHandle.myUserId()) {
|
|
return ContentProvider.maybeAddUserId(mContentUri, mContentUriOwnerUserId);
|
|
}
|
|
return mContentUri;
|
|
}
|
|
|
|
/**
|
|
* @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()}
|
|
* such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility
|
|
* purpose.
|
|
*/
|
|
@NonNull
|
|
public ClipDescription getDescription() { return mDescription; }
|
|
|
|
/**
|
|
* @return An optional {@code http} or {@code https} URI that is related to this content.
|
|
*/
|
|
@Nullable
|
|
public Uri getLinkUri() { return mLinkUri; }
|
|
|
|
/**
|
|
* Update the internal state of this object to be associated with the given token.
|
|
*
|
|
* <p>TODO(yukawa): Come up with an idea to make {@link InputContentInfo} immutable.</p>
|
|
*
|
|
* @param token special URI token obtained from the system.
|
|
* @hide
|
|
*/
|
|
public void setUriToken(IInputContentUriToken token) {
|
|
if (mUriToken != null) {
|
|
throw new IllegalStateException("URI token is already set");
|
|
}
|
|
mUriToken = token;
|
|
}
|
|
|
|
/**
|
|
* Requests a temporary {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION read-only}
|
|
* access permission for the content URI associated with this object.
|
|
*
|
|
* <p>The lifecycle of the permission granted here is tied to this object instance. If the
|
|
* permission is not released explicitly via {@link #releasePermission()}, it will be
|
|
* released automatically when there are no more references to this object.</p>
|
|
*
|
|
* <p>Does nothing if the temporary permission is already granted.</p>
|
|
*/
|
|
public void requestPermission() {
|
|
if (mUriToken == null) {
|
|
return;
|
|
}
|
|
try {
|
|
mUriToken.take();
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Releases a temporary read-only access permission for content URI associated with this object.
|
|
*
|
|
* <p>Does nothing if the temporary permission is not granted.</p>
|
|
*/
|
|
public void releasePermission() {
|
|
if (mUriToken == null) {
|
|
return;
|
|
}
|
|
try {
|
|
mUriToken.release();
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used to package this object into a {@link Parcel}.
|
|
*
|
|
* @param dest The {@link Parcel} to be written.
|
|
* @param flags The flags used for parceling.
|
|
*/
|
|
@Override
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
Uri.writeToParcel(dest, mContentUri);
|
|
dest.writeInt(mContentUriOwnerUserId);
|
|
mDescription.writeToParcel(dest, flags);
|
|
Uri.writeToParcel(dest, mLinkUri);
|
|
if (mUriToken != null) {
|
|
dest.writeInt(1);
|
|
dest.writeStrongBinder(mUriToken.asBinder());
|
|
} else {
|
|
dest.writeInt(0);
|
|
}
|
|
}
|
|
|
|
private InputContentInfo(@NonNull Parcel source) {
|
|
mContentUri = Uri.CREATOR.createFromParcel(source);
|
|
mContentUriOwnerUserId = source.readInt();
|
|
mDescription = ClipDescription.CREATOR.createFromParcel(source);
|
|
mLinkUri = Uri.CREATOR.createFromParcel(source);
|
|
if (source.readInt() == 1) {
|
|
mUriToken = IInputContentUriToken.Stub.asInterface(source.readStrongBinder());
|
|
} else {
|
|
mUriToken = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used to make this class parcelable.
|
|
*/
|
|
public static final @android.annotation.NonNull Parcelable.Creator<InputContentInfo> CREATOR
|
|
= new Parcelable.Creator<InputContentInfo>() {
|
|
@Override
|
|
public InputContentInfo createFromParcel(Parcel source) {
|
|
return new InputContentInfo(source);
|
|
}
|
|
|
|
@Override
|
|
public InputContentInfo[] newArray(int size) {
|
|
return new InputContentInfo[size];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
}
|