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