diff options
Diffstat (limited to 'android/view/inputmethod/InputContentInfo.java')
-rw-r--r-- | android/view/inputmethod/InputContentInfo.java | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/android/view/inputmethod/InputContentInfo.java b/android/view/inputmethod/InputContentInfo.java new file mode 100644 index 00000000..7104a287 --- /dev/null +++ b/android/view/inputmethod/InputContentInfo.java @@ -0,0 +1,280 @@ +/* + * 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; } + + void setUriToken(IInputContentUriToken token) { + if (mUriToken != null) { + throw new IllegalStateException("URI token is already set"); + } + mUriToken = token; + } + + /** + * Requests a temporary read-only access permission for content URI associated with this object. + * + * <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 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; + } +} |