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