1 /* 2 * Copyright (C) 2011 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 com.android.providers.contacts; 18 19 import android.content.UriMatcher; 20 import android.net.Uri; 21 import android.provider.ContactsContract; 22 23 import com.google.android.collect.Lists; 24 import com.google.android.collect.Maps; 25 26 import java.util.List; 27 import java.util.Map; 28 import java.util.regex.Pattern; 29 30 /** 31 * A subclass of URI matcher with additional logic and awareness around profile-specific URIs. 32 */ 33 public class ProfileAwareUriMatcher extends UriMatcher { 34 35 private static final Pattern PATH_SPLIT_PATTERN = Pattern.compile("/"); 36 37 private static final String PROFILE_SEGMENT = "profile"; 38 private static final String LOOKUP_SEGMENT = "lookup"; 39 private static final String VCARD_SEGMENT = "as_vcard"; 40 private static final String ID_SEGMENT = "#"; 41 private static final String WILDCARD_SEGMENT = "*"; 42 43 // URIs matching to these constants must always use the profile database. 44 private static final List<Integer> PROFILE_URIS = Lists.newArrayList(); 45 46 // URIs in this map will direct to the profile database if the ID (which is at the segment 47 // path with the location specified by the value in the map) is in the profile ID-space. 48 private static final Map<Integer, Integer> PROFILE_URI_ID_MAP = Maps.newHashMap(); 49 50 // URIs in this map will direct to the profile database if the lookup key (which is at the 51 // segment path with the location specified by the value in the map) is the special profile 52 // lookup key (see {@link ProfileAggregator#PROFILE_LOOKUP_KEY}). 53 private static final Map<Integer, Integer> PROFILE_URI_LOOKUP_KEY_MAP = Maps.newHashMap(); 54 55 /** 56 * Creates the root node of the URI tree. 57 * 58 * @param code the code to match for the root URI 59 */ ProfileAwareUriMatcher(int code)60 public ProfileAwareUriMatcher(int code) { 61 super(code); 62 } 63 64 @Override addURI(String authority, String path, int code)65 public void addURI(String authority, String path, int code) { 66 super.addURI(authority, path, code); 67 68 // Do a second tokenization pass to determine whether the URI may apply to profiles. 69 if (path != null) { 70 String newPath = path; 71 // Strip leading slash if present. 72 if (path.length() > 0 && path.charAt(0) == '/') { 73 newPath = path.substring(1); 74 } 75 String[] tokens = PATH_SPLIT_PATTERN.split(newPath); 76 77 if (tokens != null) { 78 79 // Keep track of whether we've passed a "lookup" token in the path; wildcards after 80 // that token will be interpreted as lookup keys. For our purposes, vcard paths 81 // also count as lookup tokens, since the vcard is specified by lookup key. 82 boolean afterLookup = false; 83 for (int i = 0; i < tokens.length; i++) { 84 String token = tokens[i]; 85 if (token.equals(PROFILE_SEGMENT)) { 86 PROFILE_URIS.add(code); 87 return; 88 } else if (token.equals(LOOKUP_SEGMENT) 89 || token.equals(VCARD_SEGMENT)) { 90 afterLookup = true; 91 continue; 92 } else if (token.equals(ID_SEGMENT)) { 93 PROFILE_URI_ID_MAP.put(code, i); 94 } else if (token.equals(WILDCARD_SEGMENT)) { 95 if (afterLookup) { 96 PROFILE_URI_LOOKUP_KEY_MAP.put(code, i); 97 } 98 } 99 afterLookup = false; 100 } 101 } 102 } 103 } 104 105 /** 106 * Determines whether the given URI is intended for the profile DB rather than contacts. 107 * This is true under any of three conditions: 108 * 1. The URI itself is specifically for the profile (it contains a "profile" segment). 109 * 2. The URI contains ID references that are in the profile ID-space. 110 * 3. The URI contains lookup key references that match the special profile lookup key. 111 * @param uri The URI to examine. 112 * @return Whether the operation for this URI is intended for the profile DB. 113 */ mapsToProfile(Uri uri)114 public boolean mapsToProfile(Uri uri) { 115 int match = match(uri); 116 if (PROFILE_URIS.contains(match)) { 117 return true; 118 } else if (PROFILE_URI_ID_MAP.containsKey(match)) { 119 int idSegment = PROFILE_URI_ID_MAP.get(match); 120 long id = Long.parseLong(uri.getPathSegments().get(idSegment)); 121 if (ContactsContract.isProfileId(id)) { 122 return true; 123 } 124 } else if (PROFILE_URI_LOOKUP_KEY_MAP.containsKey(match)) { 125 int lookupKeySegment = PROFILE_URI_LOOKUP_KEY_MAP.get(match); 126 if (lookupKeySegment >= uri.getPathSegments().size()) { 127 return false; 128 } 129 String lookupKey = uri.getPathSegments().get(lookupKeySegment); 130 if (ContactLookupKey.PROFILE_LOOKUP_KEY.equals(lookupKey)) { 131 return true; 132 } 133 } 134 return false; 135 } 136 } 137