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