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/** 32b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * Utilities to discover and interact with partner customizations. An overlay package is one that 33b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * registers the broadcast receiver for {@code com.android.setupwizard.action.PARTNER_CUSTOMIZATION} 34b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * in its manifest. There can only be one customization APK on a device, and it must be bundled with 35b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * the system. 36d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * 37b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * <p>Derived from {@code com.android.launcher3/Partner.java} 38d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam */ 39d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lampublic class Partner { 40d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam private static final String TAG = "(SUW) Partner"; 41d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 42d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam /** Marker action used to discover partner */ 43d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam private static final String 44d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam ACTION_PARTNER_CUSTOMIZATION = "com.android.setupwizard.action.PARTNER_CUSTOMIZATION"; 45d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 46d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam private static boolean sSearched = false; 47d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam private static Partner sPartner; 48d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 49d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam /** 50d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Convenience to get a drawable from partner overlay, or if not available, the drawable from 51d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * the original context. 52d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * 53d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * @see #getResourceEntry(android.content.Context, int) 54d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam */ 55d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public static Drawable getDrawable(Context context, int id) { 56d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final ResourceEntry entry = getResourceEntry(context, id); 57d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam return entry.resources.getDrawable(entry.id); 58d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 59d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 60d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam /** 61d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Convenience to get a string from partner overlay, or if not available, the string from the 62d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * original context. 63d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * 64d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * @see #getResourceEntry(android.content.Context, int) 65d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam */ 66d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public static String getString(Context context, int id) { 67d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final ResourceEntry entry = getResourceEntry(context, id); 68d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam return entry.resources.getString(entry.id); 69d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 70d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 71d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam /** 72d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Find an entry of resource in the overlay package provided by partners. It will first look for 73d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * the resource in the overlay package, and if not available, will return the one in the 74d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * original context. 75d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * 76d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * @return a ResourceEntry in the partner overlay's resources, if one is defined. Otherwise the 77d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * resources from the original context is returned. Clients can then get the resource by 78d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * {@code entry.resources.getString(entry.id)}, or other methods available in 79d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * {@link android.content.res.Resources}. 80d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam */ 81d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public static ResourceEntry getResourceEntry(Context context, int id) { 82d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final Partner partner = Partner.get(context); 8364158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam if (partner != null) { 84d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final Resources ourResources = context.getResources(); 85d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final String name = ourResources.getResourceEntryName(id); 86d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final String type = ourResources.getResourceTypeName(id); 87d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final int partnerId = partner.getIdentifier(name, type); 8864158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam if (partnerId != 0) { 8964158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam return new ResourceEntry(partner.mResources, partnerId, true); 9064158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam } 91d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 9264158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam return new ResourceEntry(context.getResources(), id, false); 93d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 94d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 95d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public static class ResourceEntry { 96d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public Resources resources; 97d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public int id; 9864158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam public boolean isOverlay; 99d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 10064158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam ResourceEntry(Resources resources, int id, boolean isOverlay) { 101d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam this.resources = resources; 102d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam this.id = id; 10364158e33c3ad618ed0eecef71fd20dd8e3c02568Maurice Lam this.isOverlay = isOverlay; 104d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 105d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 106d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 107d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam /** 108d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * Find and return partner details, or {@code null} if none exists. A partner package is marked 109d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam * by a broadcast receiver declared in the manifest that handles the 110b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * {@code com.android.setupwizard.action.PARTNER_CUSTOMIZATION} intent action. The overlay 111b38561602db3fc1b31c7ee907da41ec2c53e4764Maurice Lam * package must also be a system package. 112d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam */ 113d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public static synchronized Partner get(Context context) { 114d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam if (!sSearched) { 115d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam PackageManager pm = context.getPackageManager(); 116d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION); 117d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) { 118d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam if (info.activityInfo == null) { 119d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam continue; 120d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 121d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final ApplicationInfo appInfo = info.activityInfo.applicationInfo; 122d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 123d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam try { 124d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam final Resources res = pm.getResourcesForApplication(appInfo); 125d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam sPartner = new Partner(appInfo.packageName, res); 126d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam break; 127d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } catch (NameNotFoundException e) { 128d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam Log.w(TAG, "Failed to find resources for " + appInfo.packageName); 129d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 130d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 131d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 132d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam sSearched = true; 133d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 134d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam return sPartner; 135d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 136d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 137d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam @VisibleForTesting 138d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public static synchronized void resetForTesting() { 139d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam sSearched = false; 140d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam sPartner = null; 141d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 142d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 143d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam private final String mPackageName; 144d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam private final Resources mResources; 145d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 146d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam private Partner(String packageName, Resources res) { 147d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam mPackageName = packageName; 148d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam mResources = res; 149d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 150d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 151d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public String getPackageName() { 152d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam return mPackageName; 153d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 154d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 155d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public Resources getResources() { 156d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam return mResources; 157d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 158d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam 159d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam public int getIdentifier(String name, String defType) { 160d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam return mResources.getIdentifier(name, defType, mPackageName); 161d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam } 162d617ee5e12914b052682ee6f1bdf3ece28392f54Maurice Lam} 163