• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package android.view.inputmethod;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.content.ClipDescription;
23 import android.content.ContentProvider;
24 import android.net.Uri;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.os.RemoteException;
28 import android.os.UserHandle;
29 
30 import com.android.internal.inputmethod.IInputContentUriToken;
31 
32 import java.security.InvalidParameterException;
33 
34 /**
35  * A container object with which input methods can send content files to the target application.
36  */
37 public final class InputContentInfo implements Parcelable {
38 
39     /**
40      * The content URI that may or may not have a user ID embedded by
41      * {@link ContentProvider#maybeAddUserId(Uri, int)}.  This always preserves the exact value
42      * specified to a constructor.  In other words, if it had user ID embedded when it was passed
43      * to the constructor, it still has the same user ID no matter if it is valid or not.
44      */
45     @NonNull
46     private final Uri mContentUri;
47     /**
48      * The user ID to which {@link #mContentUri} belongs to.  If {@link #mContentUri} already
49      * embedded the user ID when it was specified then this fields has the same user ID.  Otherwise
50      * the user ID is determined based on the process ID when the constructor is called.
51      *
52      * <p>CAUTION: If you received {@link InputContentInfo} from a different process, there is no
53      * guarantee that this value is correct and valid.  Never use this for any security purpose</p>
54      */
55     @UserIdInt
56     private final int mContentUriOwnerUserId;
57     @NonNull
58     private final ClipDescription mDescription;
59     @Nullable
60     private final Uri mLinkUri;
61     @NonNull
62     private IInputContentUriToken mUriToken;
63 
64     /**
65      * Constructs {@link InputContentInfo} object only with mandatory data.
66      *
67      * @param contentUri Content URI to be exported from the input method.
68      * This cannot be {@code null}.
69      * @param description A {@link ClipDescription} object that contains the metadata of
70      * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
71      * {@link ClipDescription#getLabel()} should be describing the content specified by
72      * {@code contentUri} for accessibility reasons.
73      */
InputContentInfo(@onNull Uri contentUri, @NonNull ClipDescription description)74     public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) {
75         this(contentUri, description, null /* link Uri */);
76     }
77 
78     /**
79      * Constructs {@link InputContentInfo} object with additional link URI.
80      *
81      * @param contentUri Content URI to be exported from the input method.
82      * This cannot be {@code null}.
83      * @param description A {@link ClipDescription} object that contains the metadata of
84      * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
85      * {@link ClipDescription#getLabel()} should be describing the content specified by
86      * {@code contentUri} for accessibility reasons.
87      * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
88      * a way to navigate the user to the specified web page if this is not {@code null}.
89      * @throws InvalidParameterException if any invalid parameter is specified.
90      */
InputContentInfo(@onNull Uri contentUri, @NonNull ClipDescription description, @Nullable Uri linkUri)91     public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description,
92             @Nullable Uri linkUri) {
93         validateInternal(contentUri, description, linkUri, true /* throwException */);
94         mContentUri = contentUri;
95         mContentUriOwnerUserId =
96                 ContentProvider.getUserIdFromUri(mContentUri, UserHandle.myUserId());
97         mDescription = description;
98         mLinkUri = linkUri;
99     }
100 
101     /**
102      * @return {@code true} if all the fields are valid.
103      * @hide
104      */
validate()105     public boolean validate() {
106         return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */);
107     }
108 
109     /**
110      * Constructs {@link InputContentInfo} object with additional link URI.
111      *
112      * @param contentUri Content URI to be exported from the input method.
113      * This cannot be {@code null}.
114      * @param description A {@link ClipDescription} object that contains the metadata of
115      * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
116      * {@link ClipDescription#getLabel()} should be describing the content specified by
117      * {@code contentUri} for accessibility reasons.
118      * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
119      * a way to navigate the user to the specified web page if this is not {@code null}.
120      * @param throwException {@code true} if this method should throw an
121      * {@link InvalidParameterException}.
122      * @throws InvalidParameterException if any invalid parameter is specified.
123      */
validateInternal(@onNull Uri contentUri, @NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException)124     private static boolean validateInternal(@NonNull Uri contentUri,
125             @NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) {
126         if (contentUri == null) {
127             if (throwException) {
128                 throw new NullPointerException("contentUri");
129             }
130             return false;
131         }
132         if (description == null) {
133             if (throwException) {
134                 throw new NullPointerException("description");
135             }
136             return false;
137         }
138         final String contentUriScheme = contentUri.getScheme();
139         if (!"content".equals(contentUriScheme)) {
140             if (throwException) {
141                 throw new InvalidParameterException("contentUri must have content scheme");
142             }
143             return false;
144         }
145         if (linkUri != null) {
146             final String scheme = linkUri.getScheme();
147             if (scheme == null ||
148                     (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
149                 if (throwException) {
150                     throw new InvalidParameterException(
151                             "linkUri must have either http or https scheme");
152                 }
153                 return false;
154             }
155         }
156         return true;
157     }
158 
159     /**
160      * @return Content URI with which the content can be obtained.
161      */
162     @NonNull
getContentUri()163     public Uri getContentUri() {
164         // Fix up the content URI when and only when the caller's user ID does not match the owner's
165         // user ID.
166         if (mContentUriOwnerUserId != UserHandle.myUserId()) {
167             return ContentProvider.maybeAddUserId(mContentUri, mContentUriOwnerUserId);
168         }
169         return mContentUri;
170     }
171 
172     /**
173      * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()}
174      * such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility
175      * purpose.
176      */
177     @NonNull
getDescription()178     public ClipDescription getDescription() { return mDescription; }
179 
180     /**
181      * @return An optional {@code http} or {@code https} URI that is related to this content.
182      */
183     @Nullable
getLinkUri()184     public Uri getLinkUri() { return mLinkUri; }
185 
setUriToken(IInputContentUriToken token)186     void setUriToken(IInputContentUriToken token) {
187         if (mUriToken != null) {
188             throw new IllegalStateException("URI token is already set");
189         }
190         mUriToken = token;
191     }
192 
193     /**
194      * Requests a temporary read-only access permission for content URI associated with this object.
195      *
196      * <p>Does nothing if the temporary permission is already granted.</p>
197      */
requestPermission()198     public void requestPermission() {
199         if (mUriToken == null) {
200             return;
201         }
202         try {
203             mUriToken.take();
204         } catch (RemoteException e) {
205             e.rethrowFromSystemServer();
206         }
207     }
208 
209     /**
210      * Releases a temporary read-only access permission for content URI associated with this object.
211      *
212      * <p>Does nothing if the temporary permission is not granted.</p>
213      */
releasePermission()214     public void releasePermission() {
215         if (mUriToken == null) {
216             return;
217         }
218         try {
219             mUriToken.release();
220         } catch (RemoteException e) {
221             e.rethrowFromSystemServer();
222         }
223     }
224 
225     /**
226      * Used to package this object into a {@link Parcel}.
227      *
228      * @param dest The {@link Parcel} to be written.
229      * @param flags The flags used for parceling.
230      */
231     @Override
writeToParcel(Parcel dest, int flags)232     public void writeToParcel(Parcel dest, int flags) {
233         Uri.writeToParcel(dest, mContentUri);
234         dest.writeInt(mContentUriOwnerUserId);
235         mDescription.writeToParcel(dest, flags);
236         Uri.writeToParcel(dest, mLinkUri);
237         if (mUriToken != null) {
238             dest.writeInt(1);
239             dest.writeStrongBinder(mUriToken.asBinder());
240         } else {
241             dest.writeInt(0);
242         }
243     }
244 
InputContentInfo(@onNull Parcel source)245     private InputContentInfo(@NonNull Parcel source) {
246         mContentUri = Uri.CREATOR.createFromParcel(source);
247         mContentUriOwnerUserId = source.readInt();
248         mDescription = ClipDescription.CREATOR.createFromParcel(source);
249         mLinkUri = Uri.CREATOR.createFromParcel(source);
250         if (source.readInt() == 1) {
251             mUriToken = IInputContentUriToken.Stub.asInterface(source.readStrongBinder());
252         } else {
253             mUriToken = null;
254         }
255     }
256 
257     /**
258      * Used to make this class parcelable.
259      */
260     public static final Parcelable.Creator<InputContentInfo> CREATOR
261             = new Parcelable.Creator<InputContentInfo>() {
262         @Override
263         public InputContentInfo createFromParcel(Parcel source) {
264             return new InputContentInfo(source);
265         }
266 
267         @Override
268         public InputContentInfo[] newArray(int size) {
269             return new InputContentInfo[size];
270         }
271     };
272 
273     /**
274      * {@inheritDoc}
275      */
276     @Override
describeContents()277     public int describeContents() {
278         return 0;
279     }
280 }
281