FontsContract.java revision b0812a30499376e09e2deb5995e998c629f24985
1b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri/*
2b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Copyright (C) 2017 The Android Open Source Project
3b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri *
4b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Licensed under the Apache License, Version 2.0 (the "License");
5b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * you may not use this file except in compliance with the License.
6b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * You may obtain a copy of the License at
7b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri *
8b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri *      http://www.apache.org/licenses/LICENSE-2.0
9b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri *
10b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Unless required by applicable law or agreed to in writing, software
11b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * distributed under the License is distributed on an "AS IS" BASIS,
12b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * See the License for the specific language governing permissions and
14b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * limitations under the License.
15b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */
16b0812a30499376e09e2deb5995e998c629f24985Clara Bayarripackage android.provider;
17b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
18b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.app.ActivityThread;
19b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.ContentResolver;
20b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.ContentUris;
21b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.Context;
22b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.pm.PackageManager;
23b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.content.pm.ProviderInfo;
24b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.database.Cursor;
25b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.graphics.fonts.FontRequest;
26b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.graphics.fonts.FontResult;
27b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.net.Uri;
28b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Bundle;
29b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Handler;
30b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.HandlerThread;
31b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.ParcelFileDescriptor;
32b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.Process;
33b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.os.ResultReceiver;
34b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport android.util.Log;
35b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
36b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport com.android.internal.annotations.GuardedBy;
37b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
38b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport java.io.FileNotFoundException;
39b0812a30499376e09e2deb5995e998c629f24985Clara Bayarriimport java.util.ArrayList;
40b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
41b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri/**
42b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri * Utility class to deal with Font ContentProviders.
43b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri */
44b0812a30499376e09e2deb5995e998c629f24985Clara Bayarripublic class FontsContract {
45b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private static final String TAG = "FontsContract";
46b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
47b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /**
48b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * Defines the constants used in a response from a Font Provider. The cursor returned from the
49b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * query should have the ID column populated with the content uri ID for the resulting font.
50b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * This should point to a real file or shared memory, as the client will mmap the given file
51b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * descriptor. Pipes, sockets and other non-mmap-able file descriptors will fail to load in the
52b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * client application.
53b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     */
54b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public static final class Columns implements BaseColumns {
55b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        /**
56b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * Constant used to request data from a font provider. The cursor returned from the query
57b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * should have this column populated with an int for the ttc index for the resulting font.
58b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         */
59b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        public static final String TTC_INDEX = "font_ttc_index";
60b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        /**
61b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * Constant used to request data from a font provider. The cursor returned from the query
62b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * may populate this column with the font variation settings String information for the
63b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * font.
64b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         */
65b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        public static final String VARIATION_SETTINGS = "font_variation_settings";
66b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        /**
67b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * Constant used to request data from a font provider. The cursor returned from the query
68b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * should have this column populated with the int style for the resulting font. This should
69b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * be one of {@link android.graphics.Typeface#NORMAL},
70b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * {@link android.graphics.Typeface#BOLD}, {@link android.graphics.Typeface#ITALIC} or
71b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         * {@link android.graphics.Typeface#BOLD_ITALIC}
72b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri         */
73b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        public static final String STYLE = "font_style";
74b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
75b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
76b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /**
77b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * Constant used to identify the List of {@link ParcelFileDescriptor} item in the Bundle
78b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * returned to the ResultReceiver in getFont.
79b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * @hide
80b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     */
81b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public static final String PARCEL_FONT_RESULTS = "font_results";
82b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
83b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /** @hide */
84b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public static final int RESULT_CODE_OK = 0;
85b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /** @hide */
86b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public static final int RESULT_CODE_FONT_NOT_FOUND = 1;
87b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /** @hide */
88b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public static final int RESULT_CODE_PROVIDER_NOT_FOUND = 2;
89b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
90b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private static final int THREAD_RENEWAL_THRESHOLD_MS = 10000;
91b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
92b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private final Context mContext;
93b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private final PackageManager mPackageManager;
94b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private final Object mLock = new Object();
95b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    @GuardedBy("mLock")
96b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private Handler mHandler;
97b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    @GuardedBy("mLock")
98b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private HandlerThread mThread;
99b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
100b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /** @hide */
101b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public FontsContract() {
102b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        // TODO: investigate if the system context is the best option here. ApplicationContext or
103b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        // the one passed by developer?
104b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        // TODO: Looks like ActivityThread.currentActivityThread() can return null. Check when it
105b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        // returns null and check if we need to handle null case.
106b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        mContext = ActivityThread.currentActivityThread().getSystemContext();
107b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        mPackageManager = mContext.getPackageManager();
108b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
109b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
110b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    // We use a background thread to post the content resolving work for all requests on. This
111b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    // thread should be quit/stopped after all requests are done.
112b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private final Runnable mReplaceDispatcherThreadRunnable = new Runnable() {
113b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        @Override
114b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        public void run() {
115b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            synchronized (mLock) {
116b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                if (mThread != null) {
117b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    mThread.quitSafely();
118b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    mThread = null;
119b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    mHandler = null;
120b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                }
121b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            }
122b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        }
123b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    };
124b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
125b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    /**
126b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     * @hide
127b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri     */
128b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    public void getFont(FontRequest request, ResultReceiver receiver) {
129b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        synchronized (mLock) {
130b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            if (mHandler == null) {
131b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                mThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND);
132b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                mThread.start();
133b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                mHandler = new Handler(mThread.getLooper());
134b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            }
135b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            mHandler.post(() -> {
136b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                String providerAuthority = request.getProviderAuthority();
137b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                // TODO: Implement cert checking for non-system apps
138b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                ProviderInfo providerInfo = mPackageManager.resolveContentProvider(
139b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        providerAuthority, PackageManager.MATCH_SYSTEM_ONLY);
140b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                if (providerInfo == null) {
141b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null);
142b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    return;
143b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                }
144b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                Bundle result = getFontFromProvider(request, receiver, providerInfo);
145b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                if (result == null) {
146b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    receiver.send(RESULT_CODE_FONT_NOT_FOUND, null);
147b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    return;
148b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                }
149b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                receiver.send(RESULT_CODE_OK, result);
150b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            });
151b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            mHandler.removeCallbacks(mReplaceDispatcherThreadRunnable);
152b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            mHandler.postDelayed(mReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS);
153b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        }
154b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
155b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri
156b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    private Bundle getFontFromProvider(FontRequest request, ResultReceiver receiver,
157b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            ProviderInfo providerInfo) {
158b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        ArrayList<FontResult> result = null;
159b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
160b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                .authority(providerInfo.authority)
161b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                .build();
162b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID,
163b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE },
164b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                "query = ?", new String[] { request.getQuery() }, null);) {
165b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            // TODO: Should we restrict the amount of fonts that can be returned?
166b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            // TODO: Write documentation explaining that all results should be from the same family.
167b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            if (cursor != null && cursor.getCount() > 0) {
168b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                result = new ArrayList<>();
169b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                final int idColumnIndex = cursor.getColumnIndex(Columns._ID);
170b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX);
171b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS);
172b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                final int styleColumnIndex = cursor.getColumnIndex(Columns.STYLE);
173b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                while (cursor.moveToNext()) {
174b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    long id = cursor.getLong(idColumnIndex);
175b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    Uri fileUri = ContentUris.withAppendedId(uri, id);
176b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    try {
177b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        ParcelFileDescriptor pfd =
178b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                                mContext.getContentResolver().openFileDescriptor(fileUri, "r");
179b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        final int ttcIndex = cursor.getInt(ttcIndexColumnIndex);
180b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        final String variationSettings = cursor.getString(vsColumnIndex);
181b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        final int style = cursor.getInt(styleColumnIndex);
182b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        result.add(new FontResult(pfd, ttcIndex, variationSettings, style));
183b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    } catch (FileNotFoundException e) {
184b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                        Log.e(TAG, "FileNotFoundException raised when interacting with content "
185b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                                + "provider " + providerInfo.authority, e);
186b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                    }
187b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri                }
188b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            }
189b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        }
190b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        if (result != null && !result.isEmpty()) {
191b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            Bundle bundle = new Bundle();
192b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result);
193b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri            return bundle;
194b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        }
195b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri        return null;
196b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri    }
197b0812a30499376e09e2deb5995e998c629f24985Clara Bayarri}
198