• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content.pm;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SystemApi;
21 import android.content.IntentFilter;
22 import android.net.Uri;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.security.MessageDigest;
27 import java.security.NoSuchAlgorithmException;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Locale;
31 
32 /**
33  * Information about an ephemeral application.
34  * @hide
35  */
36 @SystemApi
37 public final class EphemeralResolveInfo implements Parcelable {
38     /** Algorithm that will be used to generate the domain digest */
39     public static final String SHA_ALGORITHM = "SHA-256";
40 
41     private final EphemeralDigest mDigest;
42     private final String mPackageName;
43     /** The filters used to match domain */
44     private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
45 
EphemeralResolveInfo(@onNull Uri uri, @NonNull String packageName, @NonNull List<IntentFilter> filters)46     public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
47             @NonNull List<IntentFilter> filters) {
48         // validate arguments
49         if (uri == null
50                 || packageName == null
51                 || filters == null
52                 || filters.size() == 0) {
53             throw new IllegalArgumentException();
54         }
55 
56         mDigest = new EphemeralDigest(uri, 0xFFFFFFFF, -1);
57         mFilters.addAll(filters);
58         mPackageName = packageName;
59     }
60 
EphemeralResolveInfo(Parcel in)61     EphemeralResolveInfo(Parcel in) {
62         mDigest = in.readParcelable(null /*loader*/);
63         mPackageName = in.readString();
64         in.readList(mFilters, null /*loader*/);
65     }
66 
getDigestBytes()67     public byte[] getDigestBytes() {
68         return mDigest.getDigestBytes()[0];
69     }
70 
getDigestPrefix()71     public int getDigestPrefix() {
72         return mDigest.getDigestPrefix()[0];
73     }
74 
getPackageName()75     public String getPackageName() {
76         return mPackageName;
77     }
78 
getFilters()79     public List<IntentFilter> getFilters() {
80         return mFilters;
81     }
82 
83     @Override
describeContents()84     public int describeContents() {
85         return 0;
86     }
87 
88     @Override
writeToParcel(Parcel out, int flags)89     public void writeToParcel(Parcel out, int flags) {
90         out.writeParcelable(mDigest, flags);
91         out.writeString(mPackageName);
92         out.writeList(mFilters);
93     }
94 
95     public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
96             = new Parcelable.Creator<EphemeralResolveInfo>() {
97         public EphemeralResolveInfo createFromParcel(Parcel in) {
98             return new EphemeralResolveInfo(in);
99         }
100 
101         public EphemeralResolveInfo[] newArray(int size) {
102             return new EphemeralResolveInfo[size];
103         }
104     };
105 
106     /** @hide */
107     public static final class EphemeralResolveIntentInfo extends IntentFilter {
108         private final EphemeralResolveInfo mResolveInfo;
109 
EphemeralResolveIntentInfo(@onNull IntentFilter orig, @NonNull EphemeralResolveInfo resolveInfo)110         public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
111                 @NonNull EphemeralResolveInfo resolveInfo) {
112             super(orig);
113             this.mResolveInfo = resolveInfo;
114         }
115 
getEphemeralResolveInfo()116         public EphemeralResolveInfo getEphemeralResolveInfo() {
117             return mResolveInfo;
118         }
119     }
120 
121     /**
122      * Helper class to generate and store each of the digests and prefixes
123      * sent to the Ephemeral Resolver.
124      * <p>
125      * Since intent filters may want to handle multiple hosts within a
126      * domain [eg “*.google.com”], the resolver is presented with multiple
127      * hash prefixes. For example, "a.b.c.d.e" generates digests for
128      * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
129      *
130      * @hide
131      */
132     public static final class EphemeralDigest implements Parcelable {
133         /** Full digest of the domain hashes */
134         private final byte[][] mDigestBytes;
135         /** The first 4 bytes of the domain hashes */
136         private final int[] mDigestPrefix;
137 
EphemeralDigest(@onNull Uri uri, int digestMask, int maxDigests)138         public EphemeralDigest(@NonNull Uri uri, int digestMask, int maxDigests) {
139             if (uri == null) {
140                 throw new IllegalArgumentException();
141             }
142             mDigestBytes = generateDigest(uri, maxDigests);
143             mDigestPrefix = new int[mDigestBytes.length];
144             for (int i = 0; i < mDigestBytes.length; i++) {
145                 mDigestPrefix[i] =
146                         ((mDigestBytes[i][0] & 0xFF) << 24
147                                 | (mDigestBytes[i][1] & 0xFF) << 16
148                                 | (mDigestBytes[i][2] & 0xFF) << 8
149                                 | (mDigestBytes[i][3] & 0xFF) << 0)
150                         & digestMask;
151             }
152         }
153 
generateDigest(Uri uri, int maxDigests)154         private static byte[][] generateDigest(Uri uri, int maxDigests) {
155             ArrayList<byte[]> digests = new ArrayList<>();
156             try {
157                 final String host = uri.getHost().toLowerCase(Locale.ENGLISH);
158                 final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
159                 if (maxDigests <= 0) {
160                     final byte[] hostBytes = host.getBytes();
161                     digests.add(digest.digest(hostBytes));
162                 } else {
163                     int prevDot = host.lastIndexOf('.');
164                     prevDot = host.lastIndexOf('.', prevDot - 1);
165                     // shortcut for short URLs
166                     if (prevDot < 0) {
167                         digests.add(digest.digest(host.getBytes()));
168                     } else {
169                         byte[] hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
170                         digests.add(digest.digest(hostBytes));
171                         int digestCount = 1;
172                         while (prevDot >= 0 && digestCount < maxDigests) {
173                             prevDot = host.lastIndexOf('.', prevDot - 1);
174                             hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
175                             digests.add(digest.digest(hostBytes));
176                             digestCount++;
177                         }
178                     }
179                 }
180             } catch (NoSuchAlgorithmException e) {
181                 throw new IllegalStateException("could not find digest algorithm");
182             }
183             return digests.toArray(new byte[digests.size()][]);
184         }
185 
EphemeralDigest(Parcel in)186         EphemeralDigest(Parcel in) {
187             final int digestCount = in.readInt();
188             if (digestCount == -1) {
189                 mDigestBytes = null;
190             } else {
191                 mDigestBytes = new byte[digestCount][];
192                 for (int i = 0; i < digestCount; i++) {
193                     mDigestBytes[i] = in.createByteArray();
194                 }
195             }
196             mDigestPrefix = in.createIntArray();
197         }
198 
getDigestBytes()199         public byte[][] getDigestBytes() {
200             return mDigestBytes;
201         }
202 
getDigestPrefix()203         public int[] getDigestPrefix() {
204             return mDigestPrefix;
205         }
206 
207         @Override
describeContents()208         public int describeContents() {
209             return 0;
210         }
211 
212         @Override
writeToParcel(Parcel out, int flags)213         public void writeToParcel(Parcel out, int flags) {
214             if (mDigestBytes == null) {
215                 out.writeInt(-1);
216             } else {
217                 out.writeInt(mDigestBytes.length);
218                 for (int i = 0; i < mDigestBytes.length; i++) {
219                     out.writeByteArray(mDigestBytes[i]);
220                 }
221             }
222             out.writeIntArray(mDigestPrefix);
223         }
224 
225         @SuppressWarnings("hiding")
226         public static final Parcelable.Creator<EphemeralDigest> CREATOR =
227                 new Parcelable.Creator<EphemeralDigest>() {
228             public EphemeralDigest createFromParcel(Parcel in) {
229                 return new EphemeralDigest(in);
230             }
231 
232             public EphemeralDigest[] newArray(int size) {
233                 return new EphemeralDigest[size];
234             }
235         };
236     }
237 }
238