1d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam/*
2d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Copyright (C) 2015 The Android Open Source Project
3d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam *
4d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Licensed under the Apache License, Version 2.0 (the "License");
5d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * you may not use this file except in compliance with the License.
6d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * You may obtain a copy of the License at
7d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam *
8d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam *      http://www.apache.org/licenses/LICENSE-2.0
9d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam *
10d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Unless required by applicable law or agreed to in writing, software
11d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * distributed under the License is distributed on an "AS IS" BASIS,
12d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * See the License for the specific language governing permissions and
14d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * limitations under the License.
15d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam */
16d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
17d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lampackage com.android.setupwizardlib.util;
18d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
19d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.content.Context;
20d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.content.Intent;
21d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.content.pm.ApplicationInfo;
22d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.content.pm.PackageManager;
23d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.content.pm.PackageManager.NameNotFoundException;
24d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.content.pm.ResolveInfo;
25d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.content.res.Resources;
26d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.graphics.drawable.Drawable;
27d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lamimport android.util.Log;
28d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
298c10c403c063aff3f17c4949b0fe9a88536ae580Maurice Lamimport com.android.setupwizardlib.annotations.VisibleForTesting;
30d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
31d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam/**
32d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Utilities to discover and interact with partner customizations. There can only be one set of
33d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * customizations on a device, and it must be bundled with the system.
34d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam *
35d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Derived from com.android.launcher3/Partner.java
36d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam */
37d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lampublic class Partner {
38d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    private static final String TAG = "(SUW) Partner";
39d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
40d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    /** Marker action used to discover partner */
41d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    private static final String
42d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            ACTION_PARTNER_CUSTOMIZATION = "com.android.setupwizard.action.PARTNER_CUSTOMIZATION";
43d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
44d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    private static boolean sSearched = false;
45d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    private static Partner sPartner;
46d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
47d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    /**
48d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * Convenience to get a drawable from partner overlay, or if not available, the drawable from
49d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * the original context.
50d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     *
51d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * @see #getResourceEntry(android.content.Context, int)
52d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     */
53d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public static Drawable getDrawable(Context context, int id) {
54d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        final ResourceEntry entry = getResourceEntry(context, id);
55d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        return entry.resources.getDrawable(entry.id);
56d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
57d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
58d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    /**
59d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * Convenience to get a string from partner overlay, or if not available, the string from the
60d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * original context.
61d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     *
62d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * @see #getResourceEntry(android.content.Context, int)
63d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     */
64d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public static String getString(Context context, int id) {
65d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        final ResourceEntry entry = getResourceEntry(context, id);
66d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        return entry.resources.getString(entry.id);
67d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
68d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
69d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    /**
70d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * Find an entry of resource in the overlay package provided by partners. It will first look for
71d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * the resource in the overlay package, and if not available, will return the one in the
72d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * original context.
73d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     *
74d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * @return a ResourceEntry in the partner overlay's resources, if one is defined. Otherwise the
75d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * resources from the original context is returned. Clients can then get the resource by
76d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * {@code entry.resources.getString(entry.id)}, or other methods available in
77d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * {@link android.content.res.Resources}.
78d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     */
79d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public static ResourceEntry getResourceEntry(Context context, int id) {
80d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        final Partner partner = Partner.get(context);
8164158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam        if (partner != null) {
82d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            final Resources ourResources = context.getResources();
83d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            final String name = ourResources.getResourceEntryName(id);
84d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            final String type = ourResources.getResourceTypeName(id);
85d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            final int partnerId = partner.getIdentifier(name, type);
8664158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam            if (partnerId != 0) {
8764158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam                return new ResourceEntry(partner.mResources, partnerId, true);
8864158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam            }
89d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        }
9064158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam        return new ResourceEntry(context.getResources(), id, false);
91d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
92d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
93d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public static class ResourceEntry {
94d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        public Resources resources;
95d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        public int id;
9664158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam        public boolean isOverlay;
97d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
9864158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam        ResourceEntry(Resources resources, int id, boolean isOverlay) {
99d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            this.resources = resources;
100d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            this.id = id;
10164158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam            this.isOverlay = isOverlay;
102d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        }
103d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
104d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
105d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    /**
106d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * Find and return partner details, or {@code null} if none exists. A partner package is marked
107d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * by a broadcast receiver declared in the manifest that handles the
108d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * com.android.setupwizard.action.PARTNER_CUSTOMIZATION intent action. The overlay package must
109d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     * also be a system package.
110d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam     */
111d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public static synchronized Partner get(Context context) {
112d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        if (!sSearched) {
113d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            PackageManager pm = context.getPackageManager();
114d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
115d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
116d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                if (info.activityInfo == null) {
117d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                    continue;
118d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                }
119d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                final ApplicationInfo appInfo = info.activityInfo.applicationInfo;
120d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
121d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                    try {
122d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                        final Resources res = pm.getResourcesForApplication(appInfo);
123d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                        sPartner = new Partner(appInfo.packageName, res);
124d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                        break;
125d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                    } catch (NameNotFoundException e) {
126d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                        Log.w(TAG, "Failed to find resources for " + appInfo.packageName);
127d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                    }
128d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam                }
129d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            }
130d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam            sSearched = true;
131d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        }
132d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        return sPartner;
133d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
134d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
135d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    @VisibleForTesting
136d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public static synchronized void resetForTesting() {
137d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        sSearched = false;
138d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        sPartner = null;
139d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
140d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
141d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    private final String mPackageName;
142d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    private final Resources mResources;
143d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
144d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    private Partner(String packageName, Resources res) {
145d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        mPackageName = packageName;
146d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        mResources = res;
147d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
148d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
149d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public String getPackageName() {
150d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        return mPackageName;
151d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
152d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
153d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public Resources getResources() {
154d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        return mResources;
155d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
156d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam
157d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    public int getIdentifier(String name, String defType) {
158d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam        return mResources.getIdentifier(name, defType, mPackageName);
159d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam    }
160d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam}
161