VendorPolicyLoader.java revision 0434fef1f6706a0a6fc53b499f3e982f70d7142d
1/* 2 * Copyright (C) 2010 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 17package com.android.email; 18 19import com.android.email.activity.setup.AccountSetupBasics.Provider; 20 21import android.content.Context; 22import android.content.pm.ApplicationInfo; 23import android.content.pm.PackageManager.NameNotFoundException; 24import android.os.Bundle; 25import android.util.Log; 26 27import java.lang.reflect.Method; 28import java.net.URI; 29import java.net.URISyntaxException; 30 31/** 32 * A bridge class to the email vendor policy apk. 33 * 34 * <p>Email vendor policy is a system apk named "com.android.email.helper". When exists, it must 35 * contain a class called "com.android.email.policy.EmailPolicy" with a static public method 36 * <code>Bundle getPolicy(String, Bundle)</code>, which serves vendor specific configurations. 37 * 38 * <p>A vendor policy apk is optional. The email application will operate properly when none is 39 * found. 40 */ 41public class VendorPolicyLoader { 42 private static final String POLICY_PACKAGE = "com.android.email.policy"; 43 private static final String POLICY_CLASS = POLICY_PACKAGE + ".EmailPolicy"; 44 private static final String GET_POLICY_METHOD = "getPolicy"; 45 private static final Class<?>[] ARGS = new Class<?>[] {String.class, Bundle.class}; 46 47 // call keys and i/o bundle keys 48 // when there is only one parameter or return value, use call key 49 private static final String USE_ALTERNATE_EXCHANGE_STRINGS = "useAlternateExchangeStrings"; 50 private static final String GET_IMAP_ID = "getImapId"; 51 private static final String GET_IMAP_ID_USER = "getImapId.user"; 52 private static final String GET_IMAP_ID_HOST = "getImapId.host"; 53 private static final String GET_IMAP_ID_CAPA = "getImapId.capabilities"; 54 private static final String FIND_PROVIDER = "findProvider"; 55 private static final String FIND_PROVIDER_IN_URI = "findProvider.inUri"; 56 private static final String FIND_PROVIDER_IN_USER = "findProvider.inUser"; 57 private static final String FIND_PROVIDER_OUT_URI = "findProvider.outUri"; 58 private static final String FIND_PROVIDER_OUT_USER = "findProvider.outUser"; 59 private static final String FIND_PROVIDER_NOTE = "findProvider.note"; 60 61 /** Singleton instance */ 62 private static VendorPolicyLoader sInstance; 63 64 private final Method mPolicyMethod; 65 66 public static VendorPolicyLoader getInstance(Context context) { 67 if (sInstance == null) { 68 // It's okay to instantiate VendorPolicyLoader multiple times. No need to synchronize. 69 sInstance = new VendorPolicyLoader(context); 70 } 71 return sInstance; 72 } 73 74 private VendorPolicyLoader(Context context) { 75 this(context, POLICY_PACKAGE, POLICY_CLASS, false); 76 } 77 78 /** 79 * Constructor for testing, where we need to use an alternate package/class name, and skip 80 * the system apk check. 81 */ 82 /* package */ VendorPolicyLoader(Context context, String packageName, String className, 83 boolean allowNonSystemApk) { 84 if (!allowNonSystemApk && !isSystemPackage(context, packageName)) { 85 mPolicyMethod = null; 86 return; 87 } 88 89 Class<?> clazz = null; 90 Method method = null; 91 try { 92 final Context policyContext = context.createPackageContext(packageName, 93 Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); 94 final ClassLoader classLoader = policyContext.getClassLoader(); 95 clazz = classLoader.loadClass(className); 96 method = clazz.getMethod(GET_POLICY_METHOD, ARGS); 97 } catch (NameNotFoundException ignore) { 98 // Package not found -- it's okay. 99 } catch (ClassNotFoundException ignore) { 100 // Class not found -- it's okay. 101 } catch (NoSuchMethodException ignore) { 102 // Method not found -- it's okay. 103 } catch (RuntimeException e) { 104 Log.w(Email.LOG_TAG, "VendorPolicyLoader: " + e); 105 } 106 mPolicyMethod = method; 107 } 108 109 // Not private for testing 110 /* package */ static boolean isSystemPackage(Context context, String packageName) { 111 try { 112 ApplicationInfo ai = context.getPackageManager().getApplicationInfo(packageName, 0); 113 return (ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 114 } catch (NameNotFoundException e) { 115 return false; // Package not found. 116 } 117 } 118 119 /** 120 * Calls the getPolicy method in the policy apk, if one exists. This method never returns null; 121 * It returns an empty {@link Bundle} when there is no policy apk (or even if the inner 122 * getPolicy returns null). 123 */ 124 // Not private for testing 125 /* package */ Bundle getPolicy(String policy, Bundle args) { 126 Bundle ret = null; 127 if (mPolicyMethod != null) { 128 try { 129 ret = (Bundle) mPolicyMethod.invoke(null, policy, args); 130 } catch (Exception e) { 131 Log.w(Email.LOG_TAG, "VendorPolicyLoader", e); 132 } 133 } 134 return (ret != null) ? ret : Bundle.EMPTY; 135 } 136 137 /** 138 * Returns true if alternate exchange descriptive text is required. 139 * 140 * Vendor function: 141 * Select: USE_ALTERNATE_EXCHANGE_STRINGS 142 * Params: none 143 * Result: USE_ALTERNATE_EXCHANGE_STRINGS (boolean) 144 */ 145 public boolean useAlternateExchangeStrings() { 146 return getPolicy(USE_ALTERNATE_EXCHANGE_STRINGS, null) 147 .getBoolean(USE_ALTERNATE_EXCHANGE_STRINGS, false); 148 } 149 150 /** 151 * Returns additional key/value pairs for the IMAP ID string. 152 * 153 * Vendor function: 154 * Select: GET_IMAP_ID 155 * Params: GET_IMAP_ID_USER (String) 156 * GET_IMAP_ID_HOST (String) 157 * GET_IMAP_ID_CAPABILITIES (String) 158 * Result: GET_IMAP_ID (String) 159 * 160 * @param userName the server that is being contacted (e.g. "imap.server.com") 161 * @param host the server that is being contacted (e.g. "imap.server.com") 162 * @param reported capabilities, if known. null is OK 163 * @return zero or more key/value pairs, quoted and delimited by spaces. If there is 164 * nothing to add, return null. 165 */ 166 public String getImapIdValues(String userName, String host, String capabilities) { 167 Bundle params = new Bundle(); 168 params.putString(GET_IMAP_ID_USER, userName); 169 params.putString(GET_IMAP_ID_HOST, host); 170 params.putString(GET_IMAP_ID_CAPA, capabilities); 171 String result = getPolicy(GET_IMAP_ID, params).getString(GET_IMAP_ID); 172 return result; 173 } 174 175 /** 176 * Returns provider setup information for a given email address 177 * 178 * Vendor function: 179 * Select: FIND_PROVIDER 180 * Param: FIND_PROVIDER (String) 181 * Result: FIND_PROVIDER_IN_URI 182 * FIND_PROVIDER_IN_USER 183 * FIND_PROVIDER_OUT_URI 184 * FIND_PROVIDER_OUT_USER 185 * FIND_PROVIDER_NOTE 186 * 187 * @param domain The domain portion of the user's email address 188 * @return suitable Provider definition, or null if no match found 189 */ 190 public Provider findProviderForDomain(String domain) { 191 Bundle params = new Bundle(); 192 params.putString(FIND_PROVIDER, domain); 193 Bundle out = getPolicy(FIND_PROVIDER, params); 194 if (out != null && !out.isEmpty()) { 195 try { 196 Provider p = new Provider(); 197 p.id = null; 198 p.label = null; 199 p.domain = domain; 200 p.incomingUriTemplate = new URI(out.getString(FIND_PROVIDER_IN_URI)); 201 p.incomingUsernameTemplate = out.getString(FIND_PROVIDER_IN_USER); 202 p.outgoingUriTemplate = new URI(out.getString(FIND_PROVIDER_OUT_URI)); 203 p.outgoingUsernameTemplate = out.getString(FIND_PROVIDER_OUT_USER); 204 p.note = out.getString(FIND_PROVIDER_NOTE); 205 return p; 206 } catch (URISyntaxException e) { 207 Log.d(Email.LOG_TAG, "uri exception while vendor policy loads " + domain); 208 } 209 } 210 return null; 211 } 212} 213